mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge pull request #25992 from twangboy/fix_12255
Refactor win_system.py
This commit is contained in:
commit
200bff7538
2 changed files with 438 additions and 151 deletions
|
@ -1,20 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
Module for managing windows systems.
|
||||
|
||||
:depends:
|
||||
- win32net
|
||||
|
||||
Support for reboot, shutdown, etc
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
import re
|
||||
import datetime
|
||||
from datetime import datetime
|
||||
|
||||
# Import 3rd Party Libs
|
||||
try:
|
||||
from shlex import quote as _cmd_quote # pylint: disable=E0611
|
||||
import win32net
|
||||
import win32api
|
||||
import pywintypes
|
||||
from ctypes import windll
|
||||
HAS_WIN32NET_MODS = True
|
||||
except ImportError:
|
||||
from pipes import quote as _cmd_quote
|
||||
HAS_WIN32NET_MODS = False
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
from salt.modules.reg import read_value
|
||||
|
||||
# Set up logging
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -25,16 +36,23 @@ __virtualname__ = 'system'
|
|||
|
||||
def __virtual__():
|
||||
'''
|
||||
This only supports Windows
|
||||
Set the system module of the kernel is Windows
|
||||
'''
|
||||
if not salt.utils.is_windows():
|
||||
return False
|
||||
return __virtualname__
|
||||
if HAS_WIN32NET_MODS and salt.utils.is_windows():
|
||||
return __virtualname__
|
||||
return False
|
||||
|
||||
|
||||
def halt(timeout=5):
|
||||
'''
|
||||
Halt a running system
|
||||
Halt a running system.
|
||||
|
||||
:param int timeout:
|
||||
Number of seconds before halting the system.
|
||||
Default is 5 seconds.
|
||||
|
||||
:return: True is successful.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -42,7 +60,7 @@ def halt(timeout=5):
|
|||
|
||||
salt '*' system.halt
|
||||
'''
|
||||
return shutdown(timeout)
|
||||
return shutdown(timeout=timeout)
|
||||
|
||||
|
||||
def init(runlevel):
|
||||
|
@ -67,7 +85,14 @@ def init(runlevel):
|
|||
|
||||
def poweroff(timeout=5):
|
||||
'''
|
||||
Poweroff a running system
|
||||
Power off a running system.
|
||||
|
||||
:param int timeout:
|
||||
Number of seconds before powering off the system.
|
||||
Default is 5 seconds.
|
||||
|
||||
:return: True if successful
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -75,12 +100,19 @@ def poweroff(timeout=5):
|
|||
|
||||
salt '*' system.poweroff
|
||||
'''
|
||||
return shutdown(timeout)
|
||||
return shutdown(timeout=timeout)
|
||||
|
||||
|
||||
def reboot(timeout=5):
|
||||
'''
|
||||
Reboot the system
|
||||
Reboot a running system.
|
||||
|
||||
:param int timeout:
|
||||
Number of seconds before rebooting the system.
|
||||
Default is 5 seconds.
|
||||
|
||||
:return: True if successful
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -88,29 +120,70 @@ def reboot(timeout=5):
|
|||
|
||||
salt '*' system.reboot
|
||||
'''
|
||||
cmd = ['shutdown', '/r', '/t', '{0}'.format(timeout)]
|
||||
ret = __salt__['cmd.run'](cmd, python_shell=False)
|
||||
return ret
|
||||
return shutdown(timeout=timeout, reboot=True)
|
||||
|
||||
|
||||
def shutdown(timeout=5):
|
||||
def shutdown(message=None, timeout=5, force_close=True, reboot=False):
|
||||
'''
|
||||
Shutdown a running system
|
||||
Shutdown a running system.
|
||||
|
||||
CLI Example:
|
||||
:param str message:
|
||||
A message to display to the user before shutting down.
|
||||
|
||||
.. code-block:: bash
|
||||
:param int timeout:
|
||||
The length of time that the shutdown dialog box should be displayed, in
|
||||
seconds. While this dialog box is displayed, the shutdown can be stopped
|
||||
by the shutdown_abort function.
|
||||
|
||||
salt '*' system.shutdown
|
||||
If dwTimeout is not zero, InitiateSystemShutdown displays a dialog box
|
||||
on the specified computer. The dialog box displays the name of the user
|
||||
who called the function, displays the message specified by the lpMessage
|
||||
parameter, and prompts the user to log off. The dialog box beeps when it
|
||||
is created and remains on top of other windows in the system. The dialog
|
||||
box can be moved but not closed. A timer counts down the remaining time
|
||||
before a forced shutdown.
|
||||
|
||||
If dwTimeout is zero, the computer shuts down without displaying the
|
||||
dialog box, and the shutdown cannot be stopped by shutdown_abort.
|
||||
|
||||
Default is 5
|
||||
|
||||
:param bool force_close:
|
||||
True to force close all open applications. False displays a dialog box
|
||||
instructing the user to close the applications.
|
||||
|
||||
:param bool reboot:
|
||||
True restarts the computer immediately after shutdown.
|
||||
False caches to disk and safely powers down the system.
|
||||
|
||||
:return: True if successful
|
||||
:rtype: bool
|
||||
'''
|
||||
cmd = ['shutdown', '/s', '/t', '{0}'.format(timeout)]
|
||||
ret = __salt__['cmd.run'](cmd, python_shell=False)
|
||||
return ret
|
||||
if message:
|
||||
message = message.decode('utf-8')
|
||||
try:
|
||||
win32api.InitiateSystemShutdown('127.0.0.1', message, timeout,
|
||||
force_close, reboot)
|
||||
return True
|
||||
except pywintypes.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to shutdown the system')
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
|
||||
def shutdown_hard():
|
||||
'''
|
||||
Shutdown a running system with no timeout or warning
|
||||
Shutdown a running system with no timeout or warning.
|
||||
|
||||
:param int timeout:
|
||||
Number of seconds before shutting down the system.
|
||||
Default is 5 seconds.
|
||||
|
||||
:return: True if successful
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
|
@ -118,27 +191,61 @@ def shutdown_hard():
|
|||
|
||||
salt '*' system.shutdown_hard
|
||||
'''
|
||||
cmd = ['shutdown', '/p', '/f']
|
||||
ret = __salt__['cmd.run'](cmd, python_shell=False)
|
||||
return ret
|
||||
return shutdown(timeout=0)
|
||||
|
||||
|
||||
def shutdown_abort():
|
||||
'''
|
||||
Abort a shutdown. Only available while the dialog box is being
|
||||
displayed to the user. Once the shutdown has initiated, it cannot be aborted
|
||||
|
||||
:return: True if successful
|
||||
:rtype: bool
|
||||
'''
|
||||
try:
|
||||
win32api.AbortSystemShutdown('127.0.0.1')
|
||||
return True
|
||||
except pywintypes.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to abort system shutdown')
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
|
||||
def lock():
|
||||
'''
|
||||
Lock the workstation.
|
||||
|
||||
:return: True if successful
|
||||
:rtype: bool
|
||||
'''
|
||||
return windll.user32.LockWorkStation()
|
||||
|
||||
|
||||
def set_computer_name(name):
|
||||
'''
|
||||
Set the Windows computer name
|
||||
|
||||
:param str name:
|
||||
The new name to give the computer. Requires a reboot to take effect.
|
||||
|
||||
:return:
|
||||
Returns a dictionary containing the old and new names if successful.
|
||||
False if not.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt 'minion-id' system.set_computer_name 'DavesComputer'
|
||||
'''
|
||||
cmd = ('wmic computersystem where name="%COMPUTERNAME%"'
|
||||
' call rename name="{0}"'.format(name))
|
||||
log.debug('Attempting to change computer name. Cmd is: {0}'.format(cmd))
|
||||
ret = __salt__['cmd.run'](cmd, python_shell=True)
|
||||
if 'ReturnValue = 0;' in ret:
|
||||
ret = {'Computer Name': {'Current': get_computer_name()}}
|
||||
if name:
|
||||
name = name.decode('utf-8')
|
||||
|
||||
if windll.kernel32.SetComputerNameW(name):
|
||||
ret = {'Computer Name': {'Current': get_system_info()['name']}}
|
||||
pending = get_pending_computer_name()
|
||||
if pending not in (None, False):
|
||||
ret['Computer Name']['Pending'] = pending
|
||||
|
@ -154,6 +261,10 @@ def get_pending_computer_name():
|
|||
retrieving the pending computer name, ``False`` will be returned, and an
|
||||
error message will be logged to the minion log.
|
||||
|
||||
:return:
|
||||
Returns the pending name if pending restart. Returns none if not pending
|
||||
restart.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -161,26 +272,11 @@ def get_pending_computer_name():
|
|||
salt 'minion-id' system.get_pending_computer_name
|
||||
'''
|
||||
current = get_computer_name()
|
||||
cmd = ['reg', 'query',
|
||||
'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName',
|
||||
'/v', 'ComputerName']
|
||||
output = __salt__['cmd.run'](cmd, python_shell=False)
|
||||
pending = None
|
||||
for line in output.splitlines():
|
||||
try:
|
||||
pending = re.search(
|
||||
r'ComputerName\s+REG_SZ\s+(\S+)',
|
||||
line
|
||||
).group(1)
|
||||
break
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
if pending is not None:
|
||||
pending = read_value('HKLM',
|
||||
r'SYSTEM\CurrentControlSet\Control\ComputerName\ComputerName',
|
||||
'ComputerName')['vdata']
|
||||
if pending:
|
||||
return pending if pending != current else None
|
||||
|
||||
log.error('Unable to retrieve pending computer name using the '
|
||||
'following command: {0}'.format(cmd))
|
||||
return False
|
||||
|
||||
|
||||
|
@ -188,55 +284,90 @@ def get_computer_name():
|
|||
'''
|
||||
Get the Windows computer name
|
||||
|
||||
:return:
|
||||
Returns the computer name if found. Otherwise returns False
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt 'minion-id' system.get_computer_name
|
||||
'''
|
||||
cmd = ['net', 'config', 'server']
|
||||
lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
|
||||
for line in lines:
|
||||
if 'Server Name' in line:
|
||||
_, srv_name = line.split('Server Name', 1)
|
||||
return srv_name.strip().lstrip('\\')
|
||||
return False
|
||||
name = get_system_info()['name']
|
||||
return name if name else False
|
||||
|
||||
|
||||
def set_computer_desc(desc):
|
||||
def set_computer_desc(desc=None):
|
||||
'''
|
||||
Set the Windows computer description
|
||||
|
||||
:param str desc:
|
||||
The computer description
|
||||
|
||||
:return: False if it fails. Description if successful.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt 'minion-id' system.set_computer_desc 'This computer belongs to Dave!'
|
||||
'''
|
||||
cmd = ['net', 'config', 'server', u'/srvcomment:{0}'.format(salt.utils.sdecode(desc))]
|
||||
__salt__['cmd.run'](cmd, python_shell=False)
|
||||
# Make sure the system exists
|
||||
# Return an object containing current information array for the computer
|
||||
system_info = win32net.NetServerGetInfo(None, 101)
|
||||
|
||||
# If desc is passed, decode it for unicode
|
||||
if desc:
|
||||
system_info['comment'] = desc.decode('utf-8')
|
||||
else:
|
||||
return False
|
||||
|
||||
# Apply new settings
|
||||
try:
|
||||
win32net.NetServerSetInfo(None, 101, system_info)
|
||||
except win32net.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to update system')
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
return {'Computer Description': get_computer_desc()}
|
||||
|
||||
|
||||
set_computer_description = set_computer_desc
|
||||
|
||||
|
||||
def get_system_info():
|
||||
'''
|
||||
Get system information.
|
||||
|
||||
:return:
|
||||
Returns a Dictionary containing information about the system to include
|
||||
name, description, version, etc...
|
||||
:rtype: dict
|
||||
'''
|
||||
system_info = win32net.NetServerGetInfo(None, 101)
|
||||
return system_info
|
||||
|
||||
|
||||
def get_computer_desc():
|
||||
'''
|
||||
Get the Windows computer description
|
||||
|
||||
:return:
|
||||
Returns the computer description if found. Otherwise returns False
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt 'minion-id' system.get_computer_desc
|
||||
'''
|
||||
cmd = ['net', 'config', 'server']
|
||||
lines = __salt__['cmd.run'](cmd, python_shell=False).splitlines()
|
||||
for line in lines:
|
||||
if 'Server Comment' in line:
|
||||
_, desc = line.split('Server Comment', 1)
|
||||
return desc.strip()
|
||||
return False
|
||||
desc = get_system_info()['comment']
|
||||
return desc if desc else False
|
||||
|
||||
|
||||
get_computer_description = get_computer_desc
|
||||
|
||||
|
@ -246,29 +377,28 @@ def join_domain(
|
|||
username=None,
|
||||
password=None,
|
||||
account_ou=None,
|
||||
account_exists=False
|
||||
):
|
||||
account_exists=False):
|
||||
'''
|
||||
Join a computer to an Active Directory domain
|
||||
|
||||
domain
|
||||
:param str domain:
|
||||
The domain to which the computer should be joined, e.g.
|
||||
``my-company.com``
|
||||
|
||||
username
|
||||
:param str username:
|
||||
Username of an account which is authorized to join computers to the
|
||||
specified domain. Need to be either fully qualified like
|
||||
``user@domain.tld`` or simply ``user``
|
||||
|
||||
password
|
||||
:param str password:
|
||||
Password of the specified user
|
||||
|
||||
account_ou : None
|
||||
:param str account_ou:
|
||||
The DN of the OU below which the account for this computer should be
|
||||
created when joining the domain, e.g.
|
||||
``ou=computers,ou=departm_432,dc=my-company,dc=com``
|
||||
|
||||
account_exists : False
|
||||
:param bool account_exists:
|
||||
Needs to be set to ``True`` to allow re-using an existing account
|
||||
|
||||
CLI Example:
|
||||
|
@ -292,27 +422,20 @@ def join_domain(
|
|||
join_options = 3
|
||||
if account_exists:
|
||||
join_options = 1
|
||||
cmd = ('wmic /interactive:off ComputerSystem Where '
|
||||
'name="%computername%" call JoinDomainOrWorkgroup FJoinOptions={0} '
|
||||
'Name={1} UserName={2} Password="{3}"'
|
||||
).format(
|
||||
join_options,
|
||||
_cmd_quote(domain),
|
||||
_cmd_quote(username),
|
||||
password)
|
||||
if account_ou:
|
||||
# contrary to RFC#2253, 2.1, 'wmic' requires a ; as a RDN separator
|
||||
# for the DN
|
||||
account_ou = account_ou.replace(',', ';')
|
||||
add_ou = ' AccountOU="{0}"'.format(account_ou)
|
||||
cmd = cmd + add_ou
|
||||
|
||||
ret = __salt__['cmd.run'](cmd, python_shell=True)
|
||||
if 'ReturnValue = 0;' in ret:
|
||||
ret = windll.netapi32.NetJoinDomain(None,
|
||||
domain,
|
||||
account_ou,
|
||||
username,
|
||||
password,
|
||||
join_options)
|
||||
if ret == 0:
|
||||
return {'Domain': domain}
|
||||
|
||||
return_values = {
|
||||
2: 'Invalid OU or specifying OU is not supported',
|
||||
5: 'Access is denied',
|
||||
53: 'The network path was not found',
|
||||
87: 'The parameter is incorrect',
|
||||
110: 'The system cannot open the specified object',
|
||||
1323: 'Unable to update the password',
|
||||
|
@ -322,115 +445,249 @@ def join_domain(
|
|||
2691: 'The machine is already joined to the domain',
|
||||
2692: 'The machine is not currently joined to a domain',
|
||||
}
|
||||
for value in return_values:
|
||||
if 'ReturnValue = {0};'.format(value) in ret:
|
||||
log.error(return_values[value])
|
||||
log.error(return_values[ret])
|
||||
return False
|
||||
|
||||
|
||||
def _validate_datetime(newdatetime, valid_formats):
|
||||
def unjoin_domain(username=None, password=None, disable=False):
|
||||
'''
|
||||
Validate `newdatetime` against list of date/time formats understood by
|
||||
windows.
|
||||
Unjoin a computer from an Active Directory Domain
|
||||
|
||||
:param username:
|
||||
Username of an account which is authorized to join computers to the
|
||||
specified domain. Need to be either fully qualified like
|
||||
``user@domain.tld`` or simply ``user``
|
||||
|
||||
:param str password:
|
||||
Password of the specified user
|
||||
|
||||
:param bool disable:
|
||||
Disable the user account in Active Directory. True to disable.
|
||||
|
||||
:return: True if successful. False if not. Log contains error code.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt 'minion-id' system.unjoin_domain username='unjoinuser' \\
|
||||
password='unjoinpassword' disable=True
|
||||
'''
|
||||
unjoin_options = 0
|
||||
if disable:
|
||||
unjoin_options = 2
|
||||
|
||||
ret = windll.netapi32.NetUnjoinDomain(None,
|
||||
username,
|
||||
password,
|
||||
unjoin_options)
|
||||
if ret == 0:
|
||||
return True
|
||||
|
||||
return_values = {
|
||||
2: 'Invalid OU or specifying OU is not supported',
|
||||
5: 'Access is denied',
|
||||
53: 'The network path was not found',
|
||||
87: 'The parameter is incorrect',
|
||||
110: 'The system cannot open the specified object',
|
||||
1323: 'Unable to update the password',
|
||||
1326: 'Logon failure: unknown username or bad password',
|
||||
1355: 'The specified domain either does not exist or could not be contacted',
|
||||
2224: 'The account already exists',
|
||||
2691: 'The machine is already joined to the domain',
|
||||
2692: 'The machine is not currently joined to a domain',
|
||||
}
|
||||
log.error(return_values[ret])
|
||||
return False
|
||||
|
||||
|
||||
def _get_date_time_format(dt_string):
|
||||
'''
|
||||
Function that detects the date/time format for the string passed.
|
||||
|
||||
:param str dt_string:
|
||||
A date/time string
|
||||
|
||||
:return: The format of the passed dt_string
|
||||
:rtype: str
|
||||
'''
|
||||
valid_formats = [
|
||||
'%I:%M:%S %p',
|
||||
'%I:%M %p',
|
||||
'%H:%M:%S',
|
||||
'%H:%M',
|
||||
'%Y-%m-%d',
|
||||
'%m-%d-%y',
|
||||
'%m-%d-%Y',
|
||||
'%m/%d/%y',
|
||||
'%m/%d/%Y',
|
||||
'%Y/%m/%d'
|
||||
]
|
||||
for dt_format in valid_formats:
|
||||
try:
|
||||
datetime.datetime.strptime(newdatetime, dt_format)
|
||||
return True
|
||||
datetime.strptime(dt_string, dt_format)
|
||||
return dt_format
|
||||
except ValueError:
|
||||
continue
|
||||
return False
|
||||
|
||||
|
||||
def _validate_time(newtime):
|
||||
'''
|
||||
Validate `newtime` against list of time formats understood by windows.
|
||||
'''
|
||||
valid_time_formats = [
|
||||
'%I:%M:%S %p',
|
||||
'%I:%M %p',
|
||||
'%H:%M:%S',
|
||||
'%H:%M'
|
||||
]
|
||||
return _validate_datetime(newtime, valid_time_formats)
|
||||
|
||||
|
||||
def _validate_date(newdate):
|
||||
'''
|
||||
Validate `newdate` against list of date formats understood by windows.
|
||||
'''
|
||||
valid_date_formats = [
|
||||
'%Y-%m-%d',
|
||||
'%m/%d/%y',
|
||||
'%y/%m/%d'
|
||||
]
|
||||
return _validate_datetime(newdate, valid_date_formats)
|
||||
|
||||
|
||||
def get_system_time():
|
||||
'''
|
||||
Get the Windows system time
|
||||
Get the system time.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' system.get_system_time
|
||||
:return: Returns the system time in HH:MM AM/PM format.
|
||||
:rtype: str
|
||||
'''
|
||||
cmd = 'time /T'
|
||||
return __salt__['cmd.run'](cmd, python_shell=True)
|
||||
return datetime.strftime(datetime.now(), "%I:%M %p")
|
||||
|
||||
|
||||
def set_system_time(newtime):
|
||||
'''
|
||||
Set the Windows system time
|
||||
Set the system time.
|
||||
|
||||
:param str newtime:
|
||||
The time to set. Can be any of the following formats.
|
||||
- HH:MM:SS AM/PM
|
||||
- HH:MM AM/PM
|
||||
- HH:MM:SS (24 hour)
|
||||
- HH:MM (24 hour)
|
||||
|
||||
:return: Returns True if successful. Otherwise False.
|
||||
:rtype: bool
|
||||
'''
|
||||
# Parse time values from new time
|
||||
time_format = _get_date_time_format(newtime)
|
||||
dt_obj = datetime.strptime(newtime, time_format)
|
||||
|
||||
# Set time using set_system_date_time()
|
||||
return set_system_date_time(hours=int(dt_obj.strftime('%H')),
|
||||
minutes=int(dt_obj.strftime('%M')),
|
||||
seconds=int(dt_obj.strftime('%S')))
|
||||
|
||||
|
||||
def set_system_date_time(years=None,
|
||||
months=None,
|
||||
days=None,
|
||||
hours=None,
|
||||
minutes=None,
|
||||
seconds=None):
|
||||
'''
|
||||
Set the system date and time. Each argument is an element of the date, but
|
||||
not required. If an element is not passed, the current system value for that
|
||||
element will be used. For example, if you don't pass the year, the current
|
||||
system year will be used. (Used by set_system_date and set_system_time)
|
||||
|
||||
:param int years: Years digit, ie: 2015
|
||||
:param int months: Months digit: 1 - 12
|
||||
:param int days: Days digit: 1 - 31
|
||||
:param int hours: Hours digit: 0 - 23
|
||||
:param int minutes: Minutes digit: 0 - 59
|
||||
:param int seconds: Seconds digit: 0 - 59
|
||||
|
||||
:return: True if successful. Otherwise False.
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' system.set_system_time '11:31:15 AM'
|
||||
salt '*' system.set_system_date_ time 2015 5 12 11 37 53
|
||||
'''
|
||||
if not _validate_time(newtime):
|
||||
# Get the current date/time
|
||||
try:
|
||||
date_time = win32api.GetLocalTime()
|
||||
except win32api.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to get local time')
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
cmd = 'time {0}'.format(newtime)
|
||||
return not __salt__['cmd.retcode'](cmd, python_shell=True)
|
||||
|
||||
# Check for passed values. If not passed, use current values
|
||||
if not years:
|
||||
years = date_time[0]
|
||||
if not months:
|
||||
months = date_time[1]
|
||||
if not days:
|
||||
days = date_time[3]
|
||||
if not hours:
|
||||
hours = date_time[4]
|
||||
if not minutes:
|
||||
minutes = date_time[5]
|
||||
if not seconds:
|
||||
seconds = date_time[6]
|
||||
|
||||
# Create the time tuple to be passed to SetLocalTime, including day_of_week
|
||||
time_tuple = (years, months, days, hours, minutes, seconds, 0)
|
||||
|
||||
try:
|
||||
win32api.SetLocalTime(time_tuple)
|
||||
except win32api.error as exc:
|
||||
(number, context, message) = exc
|
||||
log.error('Failed to set local time')
|
||||
log.error('nbr: {0}'.format(number))
|
||||
log.error('ctx: {0}'.format(context))
|
||||
log.error('msg: {0}'.format(message))
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_system_date():
|
||||
'''
|
||||
Get the Windows system date
|
||||
|
||||
:return: Returns the system date.
|
||||
:rtype: str
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' system.get_system_date
|
||||
'''
|
||||
cmd = 'date /T'
|
||||
return __salt__['cmd.run'](cmd, python_shell=True)
|
||||
return datetime.strftime(datetime.now(), "%a %m/%d/%Y")
|
||||
|
||||
|
||||
def set_system_date(newdate):
|
||||
'''
|
||||
Set the Windows system date. Use <mm-dd-yy> format for the date.
|
||||
|
||||
:param str newdate:
|
||||
The date to set. Can be any of the following formats
|
||||
- YYYY-MM-DD
|
||||
- MM-DD-YYYY
|
||||
- MM-DD-YY
|
||||
- MM/DD/YYYY
|
||||
- MM/DD/YY
|
||||
- YYYY/MM/DD
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' system.set_system_date '03-28-13'
|
||||
'''
|
||||
if not _validate_date(newdate):
|
||||
return False
|
||||
cmd = 'date {0}'.format(newdate)
|
||||
return not __salt__['cmd.retcode'](cmd, python_shell=True)
|
||||
# Parse time values from new time
|
||||
date_format = _get_date_time_format(newdate)
|
||||
dt_obj = datetime.strptime(newdate, date_format)
|
||||
|
||||
# Set time using set_system_date_time()
|
||||
return set_system_date_time(years=int(dt_obj.strftime('%Y')),
|
||||
months=int(dt_obj.strftime('%m')),
|
||||
days=int(dt_obj.strftime('%d')))
|
||||
|
||||
|
||||
def start_time_service():
|
||||
'''
|
||||
Start the Windows time service
|
||||
|
||||
:return: True if successful. Otherwise False
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
@ -444,6 +701,9 @@ def stop_time_service():
|
|||
'''
|
||||
Stop the Windows time service
|
||||
|
||||
:return: True if successful. Otherwise False
|
||||
:rtype: bool
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
from datetime import datetime
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from salttesting import TestCase, skipIf
|
||||
|
@ -20,6 +21,16 @@ ensure_in_syspath('../../')
|
|||
# Import Salt Libs
|
||||
from salt.modules import win_system
|
||||
|
||||
# Import 3rd Party Libs
|
||||
try:
|
||||
import win32net # pylint: disable=W0611
|
||||
import win32api # pylint: disable=W0611
|
||||
import pywintypes # pylint: disable=W0611
|
||||
from ctypes import windll # pylint: disable=W0611
|
||||
HAS_WIN32NET_MODS = True
|
||||
except ImportError:
|
||||
HAS_WIN32NET_MODS = False
|
||||
|
||||
win_system.__salt__ = {}
|
||||
|
||||
|
||||
|
@ -43,6 +54,7 @@ class WinSystemTestCase(TestCase):
|
|||
self.assertEqual(win_system.init(3),
|
||||
'Not implemented on Windows at this time.')
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_poweroff(self):
|
||||
'''
|
||||
Test to poweroff a running system
|
||||
|
@ -51,6 +63,7 @@ class WinSystemTestCase(TestCase):
|
|||
with patch.object(win_system, 'shutdown', mock):
|
||||
self.assertEqual(win_system.poweroff(), 'salt')
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_reboot(self):
|
||||
'''
|
||||
Test to reboot the system
|
||||
|
@ -59,6 +72,7 @@ class WinSystemTestCase(TestCase):
|
|||
with patch.dict(win_system.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(win_system.reboot(), 'salt')
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_shutdown(self):
|
||||
'''
|
||||
Test to shutdown a running system
|
||||
|
@ -67,6 +81,7 @@ class WinSystemTestCase(TestCase):
|
|||
with patch.dict(win_system.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(win_system.shutdown(), 'salt')
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_shutdown_hard(self):
|
||||
'''
|
||||
Test to shutdown a running system with no timeout or warning
|
||||
|
@ -75,6 +90,7 @@ class WinSystemTestCase(TestCase):
|
|||
with patch.dict(win_system.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(win_system.shutdown_hard(), 'salt')
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_set_computer_name(self):
|
||||
'''
|
||||
Test to set the Windows computer name
|
||||
|
@ -94,6 +110,7 @@ class WinSystemTestCase(TestCase):
|
|||
|
||||
self.assertFalse(win_system.set_computer_name("salt"))
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_get_pending_computer_name(self):
|
||||
'''
|
||||
Test to get a pending computer name.
|
||||
|
@ -108,6 +125,7 @@ class WinSystemTestCase(TestCase):
|
|||
self.assertEqual(win_system.get_pending_computer_name(),
|
||||
'(salt)')
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_get_computer_name(self):
|
||||
'''
|
||||
Test to get the Windows computer name
|
||||
|
@ -118,6 +136,7 @@ class WinSystemTestCase(TestCase):
|
|||
|
||||
self.assertFalse(win_system.get_computer_name())
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_set_computer_desc(self):
|
||||
'''
|
||||
Test to set the Windows computer description
|
||||
|
@ -131,6 +150,7 @@ class WinSystemTestCase(TestCase):
|
|||
),
|
||||
{'Computer Description': "Salt's comp"})
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_get_computer_desc(self):
|
||||
'''
|
||||
Test to get the Windows computer description
|
||||
|
@ -141,6 +161,7 @@ class WinSystemTestCase(TestCase):
|
|||
|
||||
self.assertFalse(win_system.get_computer_desc())
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs w32net and other windows libraries')
|
||||
def test_join_domain(self):
|
||||
'''
|
||||
Test to join a computer to an Active Directory domain
|
||||
|
@ -161,10 +182,16 @@ class WinSystemTestCase(TestCase):
|
|||
'''
|
||||
Test to get system time
|
||||
'''
|
||||
mock = MagicMock(return_value="11:31:15 AM")
|
||||
with patch.dict(win_system.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(win_system.get_system_time(), '11:31:15 AM')
|
||||
tm = datetime.strftime(datetime.now(), "%I:%M %p")
|
||||
win_tm = win_system.get_system_time()
|
||||
try:
|
||||
self.assertEqual(win_tm, tm)
|
||||
except AssertionError:
|
||||
# handle race condition
|
||||
import re
|
||||
self.assertTrue(re.search(r'^\d{2}:\d{2} \w{2}$', win_tm))
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_set_system_time(self):
|
||||
'''
|
||||
Test to set system time
|
||||
|
@ -181,10 +208,10 @@ class WinSystemTestCase(TestCase):
|
|||
'''
|
||||
Test to get system date
|
||||
'''
|
||||
mock = MagicMock(return_value="03-28-13")
|
||||
with patch.dict(win_system.__salt__, {'cmd.run': mock}):
|
||||
self.assertEqual(win_system.get_system_date(), '03-28-13')
|
||||
date = datetime.strftime(datetime.now(), "%a %m/%d/%Y")
|
||||
self.assertEqual(win_system.get_system_date(), date)
|
||||
|
||||
@skipIf(not HAS_WIN32NET_MODS, 'this test needs the w32net library')
|
||||
def test_set_system_date(self):
|
||||
'''
|
||||
Test to set system date
|
||||
|
|
Loading…
Add table
Reference in a new issue