Coverage for manila/share/drivers/windows/winrm_helper.py: 95%
96 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright (c) 2015 Cloudbase Solutions SRL
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16import base64
18from oslo_concurrency import processutils
19from oslo_config import cfg
20from oslo_log import log
21from oslo_utils import importutils
22from oslo_utils import strutils
24from manila import exception
25from manila.i18n import _
26from manila import utils
28LOG = log.getLogger(__name__)
29CONF = cfg.CONF
31winrm_opts = [
32 cfg.IntOpt(
33 'winrm_conn_timeout',
34 default=60,
35 help='WinRM connection timeout.'),
36 cfg.IntOpt(
37 'winrm_operation_timeout',
38 default=60,
39 help='WinRM operation timeout.'),
40 cfg.IntOpt(
41 'winrm_retry_count',
42 default=3,
43 help='WinRM retry count.'),
44 cfg.IntOpt(
45 'winrm_retry_interval',
46 default=5,
47 help='WinRM retry interval in seconds'),
48]
50CONF.register_opts(winrm_opts)
52DEFAULT_PORT_HTTP = 5985
53DEFAULT_PORT_HTTPS = 5986
55TRANSPORT_PLAINTEXT = 'plaintext'
56TRANSPORT_SSL = 'ssl'
58winrm = None
61def setup_winrm():
62 global winrm
63 if not winrm:
64 try:
65 winrm = importutils.import_module('winrm')
66 except ImportError:
67 raise exception.ShareBackendException(
68 _("PyWinrm is not installed"))
71class WinRMHelper(object):
72 def __init__(self, configuration=None):
73 if configuration: 73 ↛ 74line 73 didn't jump to line 74 because the condition on line 73 was never true
74 configuration.append_config_values(winrm_opts)
75 self._config = configuration
76 else:
77 self._config = CONF
79 setup_winrm()
81 def _get_conn(self, server):
82 auth = self._get_auth(server)
83 conn = WinRMConnection(
84 ip=server['ip'],
85 conn_timeout=self._config.winrm_conn_timeout,
86 operation_timeout=self._config.winrm_operation_timeout,
87 **auth)
88 return conn
90 def execute(self, server, command, check_exit_code=True,
91 retry=True):
92 retries = self._config.winrm_retry_count if retry else 1
93 conn = self._get_conn(server)
95 @utils.retry(retry_param=Exception,
96 interval=self._config.winrm_retry_interval,
97 retries=retries)
98 def _execute():
99 parsed_cmd, sanitized_cmd = self._parse_command(command)
101 LOG.debug("Executing command: %s", sanitized_cmd)
102 (stdout, stderr, exit_code) = conn.execute(parsed_cmd)
104 sanitized_stdout = strutils.mask_password(stdout)
105 sanitized_stderr = strutils.mask_password(stderr)
106 LOG.debug("Executed command: %(cmd)s. Stdout: %(stdout)s. "
107 "Stderr: %(stderr)s. Exit code %(exit_code)s",
108 dict(cmd=sanitized_cmd, stdout=sanitized_stdout,
109 stderr=sanitized_stderr, exit_code=exit_code))
111 if check_exit_code and exit_code != 0:
112 raise processutils.ProcessExecutionError(
113 stdout=sanitized_stdout,
114 stderr=sanitized_stderr,
115 exit_code=exit_code,
116 cmd=sanitized_cmd)
117 return (stdout, stderr)
118 return _execute()
120 def _parse_command(self, command):
121 if isinstance(command, list) or isinstance(command, tuple): 121 ↛ 124line 121 didn't jump to line 124 because the condition on line 121 was always true
122 command = " ".join([str(c) for c in command])
124 sanitized_cmd = strutils.mask_password(command)
126 b64_command = base64.b64encode(command.encode("utf_16_le"))
127 command = ("powershell.exe -ExecutionPolicy RemoteSigned "
128 "-NonInteractive -EncodedCommand %s" % b64_command)
129 return command, sanitized_cmd
131 def _get_auth(self, server):
132 auth = {'username': server['username']}
134 if server['use_cert_auth']:
135 auth['cert_pem_path'] = server['cert_pem_path']
136 auth['cert_key_pem_path'] = server['cert_key_pem_path']
137 else:
138 auth['password'] = server['password']
139 return auth
142class WinRMConnection(object):
143 _URL_TEMPLATE = '%(protocol)s://%(ip)s:%(port)s/wsman'
145 def __init__(self, ip=None, port=None, use_ssl=False,
146 transport=None, username=None, password=None,
147 cert_pem_path=None, cert_key_pem_path=None,
148 operation_timeout=None, conn_timeout=None):
149 setup_winrm()
151 use_cert = bool(cert_pem_path and cert_key_pem_path)
152 transport = (TRANSPORT_SSL
153 if use_cert else TRANSPORT_PLAINTEXT)
155 _port = port or self._get_default_port(use_cert)
156 _url = self._get_url(ip, _port, use_cert)
158 self._conn = winrm.protocol.Protocol(
159 endpoint=_url, transport=transport,
160 username=username, password=password,
161 cert_pem=cert_pem_path, cert_key_pem=cert_key_pem_path)
162 self._conn.transport.timeout = conn_timeout
163 self._conn.set_timeout(operation_timeout)
165 def _get_default_port(self, use_ssl):
166 port = (DEFAULT_PORT_HTTPS
167 if use_ssl else DEFAULT_PORT_HTTP)
168 return port
170 def _get_url(self, ip, port, use_ssl):
171 if not ip:
172 err_msg = _("No IP provided.")
173 raise exception.ShareBackendException(msg=err_msg)
175 protocol = 'https' if use_ssl else 'http'
176 return self._URL_TEMPLATE % {'protocol': protocol,
177 'ip': ip,
178 'port': port}
180 def execute(self, cmd):
181 shell_id = None
182 cmd_id = None
184 try:
185 shell_id = self._conn.open_shell()
187 cmd_id = self._conn.run_command(shell_id, cmd)
189 (stdout,
190 stderr,
191 exit_code) = self._conn.get_command_output(shell_id, cmd_id)
192 finally:
193 if cmd_id: 193 ↛ 195line 193 didn't jump to line 195 because the condition on line 193 was always true
194 self._conn.cleanup_command(shell_id, cmd_id)
195 if shell_id: 195 ↛ 198line 195 didn't jump to line 198 because the condition on line 195 was always true
196 self._conn.close_shell(shell_id)
198 return (stdout, stderr, exit_code)