From 85ba04686924b302fc5788580b22d2300e89ee46 Mon Sep 17 00:00:00 2001 From: qgarnier Date: Wed, 2 Feb 2022 15:55:02 +0100 Subject: [PATCH] add(wsman): powershell chunk management (#3456) --- centreon-plugins/centreon/plugins/wsman.pm | 223 +++++++++++------- .../os/windows/wsman/mode/pendingreboot.pm | 159 +++++++++++++ centreon-plugins/os/windows/wsman/plugin.pm | 1 + 3 files changed, 294 insertions(+), 89 deletions(-) create mode 100644 centreon-plugins/os/windows/wsman/mode/pendingreboot.pm diff --git a/centreon-plugins/centreon/plugins/wsman.pm b/centreon-plugins/centreon/plugins/wsman.pm index 9b36aed9c..feed86599 100644 --- a/centreon-plugins/centreon/plugins/wsman.pm +++ b/centreon-plugins/centreon/plugins/wsman.pm @@ -26,22 +26,19 @@ use openwsman; use MIME::Base64; my %auth_method_map = ( - noauth => $openwsman::NO_AUTH_STR, - basic => $openwsman::BASIC_AUTH_STR, - digest => $openwsman::DIGEST_AUTH_STR, - pass => $openwsman::PASS_AUTH_STR, - ntlm => $openwsman::NTLM_AUTH_STR, - gssnegotiate => $openwsman::GSSNEGOTIATE_AUTH_STR, + noauth => $openwsman::NO_AUTH_STR, + basic => $openwsman::BASIC_AUTH_STR, + digest => $openwsman::DIGEST_AUTH_STR, + pass => $openwsman::PASS_AUTH_STR, + ntlm => $openwsman::NTLM_AUTH_STR, + gssnegotiate => $openwsman::GSSNEGOTIATE_AUTH_STR ); sub new { my ($class, %options) = @_; my $self = {}; bless $self, $class; - # $options{options} = options object - # $options{output} = output object - # $options{exit_value} = integer - + if (!defined($options{output})) { print "Class wsman: Need to specify 'output' argument.\n"; exit 3; @@ -51,20 +48,20 @@ sub new { $options{output}->option_exit(); } - $options{options}->add_options(arguments => - { "hostname|host:s" => { name => 'host' }, - "wsman-port:s" => { name => 'wsman_port', default => 5985 }, - "wsman-path:s" => { name => 'wsman_path', default => '/wsman' }, - "wsman-scheme:s" => { name => 'wsman_scheme', default => 'http' }, - "wsman-username:s" => { name => 'wsman_username' }, - "wsman-password:s" => { name => 'wsman_password' }, - "wsman-timeout:s" => { name => 'wsman_timeout', default => 30 }, - "wsman-proxy-url:s" => { name => 'wsman_proxy_url', }, - "wsman-proxy-username:s" => { name => 'wsman_proxy_username', }, - "wsman-proxy-password:s" => { name => 'wsman_proxy_password', }, - "wsman-debug" => { name => 'wsman_debug', }, - "wsman-auth-method:s" => { name => 'wsman_auth_method', default => 'basic' }, - "wsman-errors-exit:s" => { name => 'wsman_errors_exit', default => 'unknown' }, + $options{options}->add_options(arguments => { + 'hostname|host:s' => { name => 'host' }, + 'wsman-port:s' => { name => 'wsman_port', default => 5985 }, + 'wsman-path:s' => { name => 'wsman_path', default => '/wsman' }, + 'wsman-scheme:s' => { name => 'wsman_scheme', default => 'http' }, + 'wsman-username:s' => { name => 'wsman_username' }, + 'wsman-password:s' => { name => 'wsman_password' }, + 'wsman-timeout:s' => { name => 'wsman_timeout', default => 30 }, + 'wsman-proxy-url:s' => { name => 'wsman_proxy_url', }, + 'wsman-proxy-username:s' => { name => 'wsman_proxy_username', }, + 'wsman-proxy-password:s' => { name => 'wsman_proxy_password', }, + 'wsman-debug' => { name => 'wsman_debug', }, + 'wsman-auth-method:s' => { name => 'wsman_auth_method', default => 'basic' }, + 'wsman-errors-exit:s' => { name => 'wsman_errors_exit', default => 'unknown' } }); $options{options}->add_help(package => __PACKAGE__, sections => 'WSMAN OPTIONS'); @@ -75,7 +72,7 @@ sub new { $self->{error_msg} = undef; $self->{error_status} = 0; - + return $self; } @@ -86,22 +83,24 @@ sub connect { $self->{output}->add_option_msg(short_msg => "Unknown value '" . $self->{wsman_errors_exit} . "' for --wsman-errors-exit."); $self->{output}->option_exit(exit_litteral => 'unknown'); } - + openwsman::set_debug(1) if (defined($self->{wsman_params}->{wsman_debug})); - $self->{client} = new openwsman::Client::($self->{wsman_params}->{host}, $self->{wsman_params}->{wsman_port}, - $self->{wsman_params}->{wsman_path}, $self->{wsman_params}->{wsman_scheme}, - $self->{wsman_params}->{wsman_username}, $self->{wsman_params}->{wsman_password}); + $self->{client} = new openwsman::Client::( + $self->{wsman_params}->{host}, $self->{wsman_params}->{wsman_port}, + $self->{wsman_params}->{wsman_path}, $self->{wsman_params}->{wsman_scheme}, + $self->{wsman_params}->{wsman_username}, $self->{wsman_params}->{wsman_password} + ); if (!defined($self->{client})) { $self->{output}->add_option_msg(short_msg => 'Could not create client handler'); $self->{output}->option_exit(exit_litteral => $self->{wsman_errors_exit}); } - + if ($self->{wsman_params}->{wsman_scheme} eq 'https') { # Dont verify $self->{client}->transport()->set_verify_peer(0); $self->{client}->transport()->set_verify_host(0); } - + $self->{client}->transport()->set_auth_method($auth_method_map{$self->{wsman_params}->{wsman_auth_method}}); $self->{client}->transport()->set_timeout($self->{wsman_params}->{wsman_timeout}); if (defined($self->{wsman_params}->{wsman_proxy_url})) { @@ -119,38 +118,38 @@ sub execute_winshell_commands { my ($dont_quit) = (defined($options{dont_quit}) && $options{dont_quit} == 1) ? 1 : 0; $self->set_error(); - + my $uri = 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd'; my $namespace = 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell'; my $command_result = {}; my ($command_id, $client_options, $data, $result, $node); - + # Init result foreach my $command (@{$options{commands}}) { $command_result->{$command->{label}} = {stdout => undef, stderr => undef, exit_code => undef}; } - + if (!defined($self->{shell_id})) { $self->{shell_id} = undef; ###### # Start Shell $client_options = new openwsman::ClientOptions::() - or $self->internal_exit(msg => 'Could not create client options handler'); + or $self->internal_exit(msg => 'Could not create client options handler'); $client_options->set_timeout(30 * 1000); # 30sec $client_options->add_selector('Name', 'Themes'); $client_options->add_option('WINRS_NOPROFILE', 'FALSE'); $client_options->add_option('WINRS_CODEPAGE', '437'); # utf-8 $data = new openwsman::XmlDoc::('Shell', $namespace) - or $self->internal_exit(msg => 'Could not create XmlDoc'); + or $self->internal_exit(msg => 'Could not create XmlDoc'); $data->root()->add($namespace, 'InputStreams', 'stdin'); $data->root()->add($namespace, 'OutputStreams', 'stdout stderr'); $result = $self->{client}->create($client_options, $uri, $data->string(), length($data->string()), "utf-8"); return undef if ($self->handle_dialog_fault(result => $result, msg => 'Create failed: ', dont_quit => $dont_quit)); $node = $result->root()->find(undef, 'Selector') - or $self->internal_exit(msg => 'No shell id returned'); + or $self->internal_exit(msg => 'No shell id returned'); $self->{shell_id} = $node->text(); } @@ -158,58 +157,64 @@ sub execute_winshell_commands { ####### # Issue command $client_options = new openwsman::ClientOptions::() - or $self->internal_exit(msg => 'Could not create client options handler'); + or $self->internal_exit(msg => 'Could not create client options handler'); $client_options->set_timeout(30 * 1000); # 30sec $client_options->add_option('WINRS_CONSOLEMODE_STDIN', 'TRUE'); - $client_options->add_option('WINRS_SKIP_CMD_SHELL', 'FALSE'); + if (defined($options{skip_cmd})) { + $client_options->add_option('WINRS_SKIP_CMD_SHELL', 'TRUE'); + } else { + $client_options->add_option('WINRS_SKIP_CMD_SHELL', 'FALSE'); + } $client_options->add_selector('ShellId', $self->{shell_id}); $data = new openwsman::XmlDoc::('CommandLine', $namespace) - or $self->internal_exit(msg => 'Could not create XmlDoc'); + or $self->internal_exit(msg => 'Could not create XmlDoc'); $data->root()->add($namespace, 'Command', $command->{value}); - $result = $self->{client}->invoke($client_options, $uri, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command', - $data); + $result = $self->{client}->invoke( + $client_options, $uri, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command', + $data + ); return undef if ($self->handle_dialog_fault(result => $result, msg => 'Invoke failed: ', dont_quit => $dont_quit)); $node = $result->root()->find(undef, 'CommandId') - or $self->internal_exit(msg => 'No command id returned'); + or $self->internal_exit(msg => 'No command id returned'); $command_id = $node->text(); - + ####### # Request stdout/stderr $client_options = new openwsman::ClientOptions::() - or $self->internal_exit(msg => 'Could not create client options handler'); + or $self->internal_exit(msg => 'Could not create client options handler'); $client_options->set_timeout(30 * 1000); # 30sec $client_options->add_selector('ShellId', $self->{shell_id}); $data = new openwsman::XmlDoc::('Receive', $namespace); $node = $data->root()->add($namespace, 'DesiredStream', 'stdout stderr') - or $self->internal_exit(msg => 'Could not create XmlDoc'); + or $self->internal_exit(msg => 'Could not create XmlDoc'); $node->attr_add(undef, 'CommandId', $command_id); - + my $timeout_global = 30; #seconds my $wait_timeout_done = 0; my $loop_out = 1; my ($current_stdout, $current_stderr) = ('', ''); - + while ($loop_out == 1 && $wait_timeout_done < $timeout_global) { - - - $result = $self->{client}->invoke($client_options, $uri, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive', - $data); + $result = $self->{client}->invoke( + $client_options, $uri, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive', + $data + ); return undef if ($self->handle_dialog_fault(result => $result, msg => 'Invoke failed: ', dont_quit => $dont_quit)); my $response = $result->root()->find($namespace, 'ReceiveResponse') - or $self->internal_exit(msg => 'No ReceiveResponse'); + or $self->internal_exit(msg => 'No ReceiveResponse'); ###### # Parsing Reponse for (my $cnt = 0; $cnt < $response->size(); $cnt++) { my $node = $response->get($cnt); my $node_command = $node->attr_find(undef, 'CommandId') - or $self->internal_exit(msg => 'No CommandId in ReceiveResponse'); + or $self->internal_exit(msg => 'No CommandId in ReceiveResponse'); if ($node_command->value() ne $command_id) { $self->internal_exit(msg => 'Wrong CommandId in ReceiveResponse node'); } - + if ($node->name() eq 'Stream') { my $node_tmp = $node->attr_find(undef, 'Name'); if (!defined($node_tmp)) { @@ -253,35 +258,35 @@ sub execute_winshell_commands { $self->internal_exit(msg => 'Unknown command state: ' . $state); } } - } - } - + if ($loop_out == 1) { $self->internal_exit(msg => 'Command to long to execute...'); } - + $current_stderr =~ s/\r//mg; $current_stdout =~ s/\r//mg; - + $command_result->{$command->{label}}->{stderr} = $current_stderr if ($current_stderr ne ''); $command_result->{$command->{label}}->{stdout} = $current_stdout if ($current_stdout ne ''); - + # # terminate shell command # # not strictly needed for WinRM 2.0, but WinRM 1.1 requires this # $data = new openwsman::XmlDoc::('Signal', $namespace) - or $self->internal_exit(msg => 'Could not create XmlDoc'); + or $self->internal_exit(msg => 'Could not create XmlDoc'); $data->root()->attr_add(undef, 'CommandId', $command_id); $data->root()->add($namespace, 'Code', 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate'); - $result = $self->{client}->invoke($client_options, $uri, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal', - $data); + $result = $self->{client}->invoke( + $client_options, $uri, 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal', + $data + ); return undef if ($self->handle_dialog_fault(result => $result, msg => 'Invoke failed: ', dont_quit => $dont_quit)); } - + # Delete Shell resource if (defined($self->{shell_id}) && !(defined($options{keep_open}) && $options{keep_open} == 1)) { my $client_options = new openwsman::ClientOptions::() @@ -296,6 +301,49 @@ sub execute_winshell_commands { return $command_result; } +sub execute_powershell { + my ($self, %options) = @_; + + my $chunk = 8000; + my $filename = $options{label} . '-' . sprintf("%08X", rand(0xFFFFFFFF)) . '.bat'; + my $commands = [ + { + label => 'prev1-' . $options{label}, + value => 'if not exist "C:\\temp\\" mkdir C:\\temp' + }, + { + label => 'prev2-' . $options{label}, + value => '(echo @ECHO OFF) >> ' . $filename + }, + { + label => 'prev3-' . $options{label}, + value => 'echo|set /P="powershell -encodedcommand ">> ' . $filename + } + ]; + my $i = 0; + foreach (unpack('(A' . $chunk . ')*', $options{content})) { + push @$commands, { + label => 'chunk-' . $i . '-' . $options{label}, + value => 'echo|set /P="' . $_ . '">>' . $filename + }; + } + + push @$commands, + { + label => $options{label}, + value => $filename + }, + { + label => 'del-' . $options{label}, + value => 'del /f ' . $filename + }; + + return $self->execute_winshell_commands( + commands => $commands, + keep_open => 1 + ); +} + sub request { my ($self, %options) = @_; # $options{nothing_quit} = integer @@ -304,23 +352,23 @@ sub request { # $options{wql_filter} = string # $options{result_type} = string ('array' or 'hash' with a key) # $options{hash_key} = string - + my ($dont_quit) = (defined($options{dont_quit}) && $options{dont_quit} == 1) ? 1 : 0; my ($nothing_quit) = (defined($options{nothing_quit}) && $options{nothing_quit} == 1) ? 1 : 0; my ($result_type) = (defined($options{result_type}) && $options{result_type} =~ /^(array|hash)$/) ? $options{result_type} : 'array'; $self->set_error(); - + ###### # Check options if (!defined($options{uri})) { $self->{output}->add_option_msg(short_msg => 'Need to specify uri option'); $self->{output}->option_exit(exit_litteral => $self->{wsman_errors_exit}); } - + ###### # ClientOptions object my $client_options = new openwsman::ClientOptions::() - or $self->internal_exit(msg => 'Could not create client options handler'); + or $self->internal_exit(msg => 'Could not create client options handler'); # Optimization $client_options->set_flags($openwsman::FLAG_ENUMERATION_OPTIMIZATION); @@ -331,7 +379,7 @@ sub request { my $filter; if (defined($options{wql_filter})) { $filter = new openwsman::Filter::() - or $self->internal_exit(msg => 'Could not create filter'); + or $self->internal_exit(msg => 'Could not create filter'); $filter->wql($options{wql_filter}); } @@ -341,7 +389,7 @@ sub request { ###### # Fetch values my ($array_return, $hash_return); - + $array_return = [] if ($result_type eq 'array'); $hash_return = {} if ($result_type eq 'hash'); my $context; @@ -349,7 +397,7 @@ sub request { while (1) { my $nodes = $result->body()->find(undef, "Items"); - + # Get items. my $items; for (my $cnt = 0; defined($nodes) && ($cnt<$nodes->size()); $cnt++) { @@ -361,22 +409,20 @@ sub request { push @{$array_return}, $row_return if ($result_type eq 'array'); $hash_return->{$row_return->{$options{hash_key}}} = $row_return if ($result_type eq 'hash'); } - - $context = $result->context() - or last; - $result = $self->{client}->pull($client_options, $filter, $options{uri}, $context) - or last; + $context = $result->context() or last; + $result = $self->{client}->pull($client_options, $filter, $options{uri}, $context) + or last; } # Release context. $self->{client}->release($client_options, $options{uri}, $context) if($context); - + if ($nothing_quit == 1 && $total == 0) { $self->{output}->add_option_msg(short_msg => "Cant get a single value."); $self->{output}->option_exit(exit_litteral => $self->{option_results}->{wsman_errors_exit}); } - + if ($result_type eq 'array') { return $array_return; } @@ -427,7 +473,7 @@ sub handle_dialog_fault { my ($self, %options) = @_; my $result = $options{result}; my $msg = $options{msg}; - + unless($result && $result->is_fault eq 0) { my $fault_string = $self->{client}->fault_string(); my $msg = 'Could not enumerate instances: ' . ((defined($fault_string)) ? $fault_string : 'use debug option to have details'); @@ -438,13 +484,13 @@ sub handle_dialog_fault { $self->{output}->add_option_msg(short_msg => $msg); $self->{output}->option_exit(exit_litteral => $self->{wsman_errors_exit}); } - + return 0; } sub internal_exit { my ($self, %options) = @_; - + $self->{output}->add_option_msg(short_msg => $options{msg}); $self->{output}->option_exit(exit_litteral => $self->{wsman_errors_exit}); } @@ -453,28 +499,27 @@ sub set_error { my ($self, %options) = @_; # $options{error_msg} = string error # $options{error_status} = integer status - + $self->{error_status} = defined($options{error_status}) ? $options{error_status} : 0; $self->{error_msg} = defined($options{error_msg}) ? $options{error_msg} : undef; } sub error_status { - my ($self) = @_; - + my ($self) = @_; + return $self->{error_status}; } sub error { my ($self) = @_; - + return $self->{error_msg}; } sub get_hostname { my ($self) = @_; - my $host = $self->{wsman_params}->{host}; - return $host; + return $self->{wsman_params}->{host}; } sub get_port { @@ -559,4 +604,4 @@ Exit code for wsman Errors (default: unknown) B. -=cut \ No newline at end of file +=cut diff --git a/centreon-plugins/os/windows/wsman/mode/pendingreboot.pm b/centreon-plugins/os/windows/wsman/mode/pendingreboot.pm new file mode 100644 index 000000000..bb7758e2a --- /dev/null +++ b/centreon-plugins/os/windows/wsman/mode/pendingreboot.pm @@ -0,0 +1,159 @@ +# +# Copyright 2022 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package os::windows::wsman::mode::pendingreboot; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::misc; +use centreon::common::powershell::windows::pendingreboot; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use JSON::XS; + +sub custom_status_output { + my ($self, %options) = @_; + + return sprintf( + "'%s': reboot pending is %s [Windows Update: %s][Component Based Servicing: %s][SCCM Client: %s][File Rename Operations: %s][Computer Name Change: %s]", + $self->{result_values}->{WindowsVersion}, + $self->{result_values}->{RebootPending}, + $self->{result_values}->{WindowsUpdate}, + $self->{result_values}->{CBServicing}, + $self->{result_values}->{CCMClientSDK}, + $self->{result_values}->{PendFileRename}, + $self->{result_values}->{PendComputerRename} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'pendingreboot', type => 0 }, + ]; + + $self->{maps_counters}->{pendingreboot} = [ + { label => 'status', type => 2, warning_status => '%{RebootPending} =~ /true/i', set => { + key_values => [ + { name => 'WindowsVersion' }, { name => 'CBServicing' }, { name => 'RebootPending' }, { name => 'WindowsUpdate' }, + { name => 'CCMClientSDK' }, { name => 'PendComputerRename' }, { name => 'PendFileRename' } + ], + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'ps-exec-only' => { name => 'ps_exec_only' }, + 'ps-display' => { name => 'ps_display' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $ps = centreon::common::powershell::windows::pendingreboot::get_powershell(); + if (defined($self->{option_results}->{ps_display})) { + $self->{output}->output_add( + severity => 'OK', + short_msg => $ps + ); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); + } + + my $result = $options{wsman}->execute_powershell( + label => 'pendingreboot', + content => centreon::plugins::misc::powershell_encoded($ps) + ); + + if (defined($self->{option_results}->{ps_exec_only})) { + $self->{output}->output_add( + severity => 'OK', + short_msg => $result->{pendingreboot}->{stdout} + ); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); + } + + my $decoded; + eval { + $decoded = JSON::XS->new->decode($self->{output}->decode($result->{pendingreboot}->{stdout})); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); + $self->{output}->option_exit(); + } + + #{ WindowsVersion: "Microsoft Windows 2003 Server", CBServicing: false, WindowsUpdate: false, CCMClientSDK: null, PendComputerRename: false, PendFileRename: false, PendFileRenVal: null, RebootPending: false } + foreach (keys %$decoded) { + $decoded->{$_} = '-' if (!defined($decoded->{$_})); + $decoded->{$_} = 'true' if ($decoded->{$_} =~ /^(?:true|1)$/i); + $decoded->{$_} = 'false' if ($decoded->{$_} =~ /^(?:false|0)$/i); + } + + $self->{pendingreboot} = $decoded; +} + +1; + +__END__ + +=head1 MODE + +Check windows pending reboot. + +=over 8 + +=item B<--ps-display> + +Display powershell script. + +=item B<--ps-exec-only> + +Print powershell output. + +=item B<--warning-status> + +Set warning threshold for status (Default: '%{RebootPending} =~ /true/i'). +Can used special variables like: %{RebootPending}, %{WindowsUpdate}, %{CBServicing}, %{CCMClientSDK}, +%{PendFileRename}, %{PendComputerRename}. + +=item B<--critical-status> + +Set critical threshold for status (Default: ''). +Can used special variables like: %{RebootPending}, %{WindowsUpdate}, %{CBServicing}, %{CCMClientSDK}, +%{PendFileRename}, %{PendComputerRename}. + +=back + +=cut diff --git a/centreon-plugins/os/windows/wsman/plugin.pm b/centreon-plugins/os/windows/wsman/plugin.pm index cd87b94b4..11c9dca04 100644 --- a/centreon-plugins/os/windows/wsman/plugin.pm +++ b/centreon-plugins/os/windows/wsman/plugin.pm @@ -40,6 +40,7 @@ sub new { 'list-storages' => 'os::windows::wsman::mode::liststorages', 'memory' => 'os::windows::wsman::mode::memory', 'pages' => 'os::windows::wsman::mode::pages', + 'pending-reboot' => 'os::windows::wsman::mode::pendingreboot', 'processes' => 'os::windows::wsman::mode::processes', 'services' => 'os::windows::wsman::mode::services', 'storages' => 'os::windows::wsman::mode::storages',