From b0af32346d0808da2ae1537fd87a0cb75d99b573 Mon Sep 17 00:00:00 2001 From: twangboy Date: Tue, 31 May 2016 13:53:33 -0600 Subject: [PATCH 1/3] Add additional params to install and remove --- salt/modules/win_servermanager.py | 65 +++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/salt/modules/win_servermanager.py b/salt/modules/win_servermanager.py index 4ca67d86e1..4344ad8423 100644 --- a/salt/modules/win_servermanager.py +++ b/salt/modules/win_servermanager.py @@ -83,8 +83,9 @@ def list_available(): salt '*' win_servermanager.list_available ''' cmd = 'Import-Module ServerManager; ' \ - 'Get-WindowsFeature -erroraction silentlycontinue ' \ - '-warningaction silentlycontinue' + 'Get-WindowsFeature ' \ + '-ErrorAction SilentlyContinue ' \ + '-WarningAction SilentlyContinue' return __salt__['cmd.shell'](cmd, shell='powershell') @@ -102,9 +103,10 @@ def list_installed(): salt '*' win_servermanager.list_installed ''' - cmd = 'Get-WindowsFeature -erroraction silentlycontinue ' \ - '-warningaction silentlycontinue | ' \ - 'Select DisplayName,Name,Installed' + cmd = 'Get-WindowsFeature ' \ + '-ErrorAction SilentlyContinue ' \ + '-WarningAction SilentlyContinue ' \ + '| Select DisplayName,Name,Installed' features = _pshell_json(cmd) ret = {} @@ -115,7 +117,7 @@ def list_installed(): return ret -def install(feature, recurse=False): +def install(feature, recurse=False, source=None, restart=False): ''' Install a feature @@ -129,7 +131,13 @@ def install(feature, recurse=False): :param str feature: The name of the feature to install - :param bool recurse: Install all sub-features + :param bool recurse: Install all sub-features. Default is False + + :param str source: Path to the source files if missing from the target + system. Default is None + + :param bool restart: Restarts the computer when installation is complete, if + required by the role/feature installed. Default is False :return: A dictionary containing the results of the install :rtype: dict @@ -141,13 +149,26 @@ def install(feature, recurse=False): salt '*' win_servermanager.install Telnet-Client salt '*' win_servermanager.install SNMP-Service True ''' + mgmt_tools = '' + if salt.utils.version_cmp(__grains__['osversion'], '6.2') >= 0: + mgmt_tools = '-IncludeManagementTools' + sub = '' if recurse: sub = '-IncludeAllSubFeature' - cmd = 'Add-WindowsFeature -Name {0} {1} ' \ + rst = '' + if restart: + rst = '-Restart' + + src = '' + if source is not None: + src = '-Source {0}'.format(source) + + cmd = 'Add-WindowsFeature -Name {0} {1} {2} {3} {4} ' \ '-ErrorAction SilentlyContinue ' \ - '-WarningAction SilentlyContinue'.format(_cmd_quote(feature), sub) + '-WarningAction SilentlyContinue'\ + .format(_cmd_quote(feature), mgmt_tools, sub, src, rst) out = _pshell_json(cmd) if out['FeatureResult']: @@ -162,7 +183,7 @@ def install(feature, recurse=False): 'Success': out['Success']} -def remove(feature): +def remove(feature, remove_payload=False, restart=False): ''' Remove an installed feature @@ -175,6 +196,13 @@ def remove(feature): :param str feature: The name of the feature to remove + :param bool remove_payload: True will cause the features to be removed from + the side-by-side store (``%SystemDrive%:\Windows\WinSxS``). Default is + False + + :param bool restart: Restarts the computer when uninstall is complete, if + required by the role/feature removed. Default is False + :return: A dictionary containing the results of the uninstall :rtype: dict @@ -184,9 +212,22 @@ def remove(feature): salt -t 600 '*' win_servermanager.remove Telnet-Client ''' - cmd = 'Remove-WindowsFeature -Name {0} ' \ + mgmt_tools = '' + if salt.utils.version_cmp(__grains__['osversion'], '6.2') >= 0: + mgmt_tools = '-IncludeManagementTools' + + rmv = '' + if remove_payload: + rmv = '-Remove' + + rst = '' + if restart: + rst = '-Restart' + + cmd = 'Remove-WindowsFeature -Name {0} {1} {2} {3} ' \ '-ErrorAction SilentlyContinue ' \ - '-WarningAction SilentlyContinue'.format(_cmd_quote(feature)) + '-WarningAction SilentlyContinue'\ + .format(_cmd_quote(feature), mgmt_tools, rmv, rst) out = _pshell_json(cmd) if out['FeatureResult']: From 4775e6bdf0ea78f08f12d623e5d671182f0e7cc9 Mon Sep 17 00:00:00 2001 From: twangboy Date: Tue, 31 May 2016 15:00:55 -0600 Subject: [PATCH 2/3] Add additional params to state --- salt/modules/win_servermanager.py | 6 ++- salt/states/win_servermanager.py | 67 ++++++++++++++++++------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/salt/modules/win_servermanager.py b/salt/modules/win_servermanager.py index 4344ad8423..f9efb2016a 100644 --- a/salt/modules/win_servermanager.py +++ b/salt/modules/win_servermanager.py @@ -134,7 +134,8 @@ def install(feature, recurse=False, source=None, restart=False): :param bool recurse: Install all sub-features. Default is False :param str source: Path to the source files if missing from the target - system. Default is None + system. None means that the system will use windows update services to + find the required files. Default is None :param bool restart: Restarts the computer when installation is complete, if required by the role/feature installed. Default is False @@ -148,6 +149,7 @@ def install(feature, recurse=False, source=None, restart=False): salt '*' win_servermanager.install Telnet-Client salt '*' win_servermanager.install SNMP-Service True + salt '*' win_servermanager.install TFTP-Client source=d:\\side-by-side ''' mgmt_tools = '' if salt.utils.version_cmp(__grains__['osversion'], '6.2') >= 0: @@ -196,7 +198,7 @@ def remove(feature, remove_payload=False, restart=False): :param str feature: The name of the feature to remove - :param bool remove_payload: True will cause the features to be removed from + :param bool remove_payload: True will cause the feature to be removed from the side-by-side store (``%SystemDrive%:\Windows\WinSxS``). Default is False diff --git a/salt/states/win_servermanager.py b/salt/states/win_servermanager.py index cda08515e4..3696037606 100644 --- a/salt/states/win_servermanager.py +++ b/salt/states/win_servermanager.py @@ -14,29 +14,33 @@ def __virtual__(): return 'win_servermanager' if 'win_servermanager.install' in __salt__ else False -def installed(name, recurse=False, force=False): +def installed(name, recurse=False, force=False, source=None, restart=False): ''' Install the windows feature - name: - short name of the feature (the right column in win_servermanager.list_available) - - recurse: - install all sub-features as well - - force: - if the feature is installed but on of its sub-features are not installed set this to True to force - the installation of the sub-features + Args: + name (str): Short name of the feature (the right column in + win_servermanager.list_available) + recurse (Optional[bool]): install all sub-features as well + force (Optional[bool]): if the feature is installed but one of its + sub-features are not installed set this to True to force the + installation of the sub-features + source (Optional[str]): Path to the source files if missing from the + target system. None means that the system will use windows update + services to find the required files. Default is None + restart (Optional[bool]): Restarts the computer when installation is + complete, if required by the role/feature installed. Default is + False Note: - Some features require reboot after un/installation. If so, until the server is restarted - other features can not be installed! + Some features require reboot after un/installation. If so, until the + server is restarted other features can not be installed! Example: - - Run ``salt MinionName win_servermanager.list_available`` to get a list of available roles and features. Use - the name in the right column. Do not use the role or feature names mentioned in the PKGMGR documentation. In - this example for IIS-WebServerRole the name to be used is Web-Server. + Run ``salt MinionName win_servermanager.list_available`` to get a list + of available roles and features. Use the name in the right column. Do + not use the role or feature names mentioned in the PKGMGR documentation. + In this example for IIS-WebServerRole the name to be used is Web-Server. .. code-block:: yaml @@ -73,7 +77,8 @@ def installed(name, recurse=False, force=False): ret['changes'] = {} # Install the features - status = __salt__['win_servermanager.install'](name, recurse) + status = __salt__['win_servermanager.install']( + name, recurse, source, restart) ret['result'] = status['Success'] if not ret['result']: @@ -91,23 +96,27 @@ def installed(name, recurse=False, force=False): return ret -def removed(name): +def removed(name, remove_payload=False, restart=False): ''' Remove the windows feature - name: - short name of the feature (the right column in win_servermanager.list_available) + Args: + name (str): Short name of the feature (the right column in + win_servermanager.list_available) + remove_payload (Optional[bool]): True will case the feature to be + removed from the side-by-side store + restart (Optional[bool]): Restarts the computer when uninstall is + complete, if required by the role/feature removed. Default is False - .. note:: - - Some features require a reboot after uninstallation. If so the feature will not be completely uninstalled until - the server is restarted. + Note: + Some features require a reboot after uninstallation. If so the feature + will not be completely uninstalled until the server is restarted. Example: - - Run ``salt MinionName win_servermanager.list_installed`` to get a list of all features installed. Use the top - name listed for each feature, not the indented one. Do not use the role or feature names mentioned in the - PKGMGR documentation. + Run ``salt MinionName win_servermanager.list_installed`` to get a list + of all features installed. Use the top name listed for each feature, not + the indented one. Do not use the role or feature names mentioned in the + PKGMGR documentation. .. code-block:: yaml @@ -134,7 +143,7 @@ def removed(name): ret['changes'] = {} # Remove the features - status = __salt__['win_servermanager.remove'](name) + status = __salt__['win_servermanager.remove'](name, remove_payload, restart) ret['result'] = status['Success'] if not ret['result']: From 6c7b21676adcc60c1c8e99b642321cb9a4331fda Mon Sep 17 00:00:00 2001 From: twangboy Date: Tue, 31 May 2016 17:34:13 -0600 Subject: [PATCH 3/3] Fix lint and tests --- salt/modules/win_servermanager.py | 2 +- tests/unit/modules/win_servermanager_test.py | 35 ++++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/salt/modules/win_servermanager.py b/salt/modules/win_servermanager.py index f9efb2016a..65c0599aa8 100644 --- a/salt/modules/win_servermanager.py +++ b/salt/modules/win_servermanager.py @@ -186,7 +186,7 @@ def install(feature, recurse=False, source=None, restart=False): def remove(feature, remove_payload=False, restart=False): - ''' + r''' Remove an installed feature .. note:: diff --git a/tests/unit/modules/win_servermanager_test.py b/tests/unit/modules/win_servermanager_test.py index 1cfb22122c..e523c6f9dd 100644 --- a/tests/unit/modules/win_servermanager_test.py +++ b/tests/unit/modules/win_servermanager_test.py @@ -18,6 +18,7 @@ from salt.modules import win_servermanager # Globals win_servermanager.__salt__ = {} +win_servermanager.__grains__ = {} @skipIf(NO_MOCK, NO_MOCK_REASON) @@ -56,13 +57,16 @@ class WinServermanagerTestCase(TestCase): 'FeatureResult': [{'DisplayName': 'Spongebob', 'RestartNeeded': False}]}) - with patch.object(win_servermanager, '_pshell_json', mock): - expected = {'ExitCode': 0, - 'DisplayName': 'Spongebob', - 'RestartNeeded': False, - 'Success': True} - self.assertDictEqual( - win_servermanager.install('Telnet-Client'), expected) + grain_mock = MagicMock(return_value='10.0.15130') + with patch.dict( + win_servermanager.__grains__, {'osversion': grain_mock}): + with patch.object(win_servermanager, '_pshell_json', mock): + expected = {'ExitCode': 0, + 'DisplayName': 'Spongebob', + 'RestartNeeded': False, + 'Success': True} + self.assertDictEqual( + win_servermanager.install('Telnet-Client'), expected) def test_remove(self): ''' @@ -73,13 +77,16 @@ class WinServermanagerTestCase(TestCase): 'FeatureResult': [{'DisplayName': 'Spongebob', 'RestartNeeded': False}]}) - with patch.object(win_servermanager, '_pshell_json', mock): - expected = {'ExitCode': 0, - 'DisplayName': 'Spongebob', - 'RestartNeeded': False, - 'Success': True} - self.assertDictEqual( - win_servermanager.remove('Telnet-Client'), expected) + grain_mock = MagicMock(return_value='10.0.15130') + with patch.dict( + win_servermanager.__grains__, {'osversion': grain_mock}): + with patch.object(win_servermanager, '_pshell_json', mock): + expected = {'ExitCode': 0, + 'DisplayName': 'Spongebob', + 'RestartNeeded': False, + 'Success': True} + self.assertDictEqual( + win_servermanager.remove('Telnet-Client'), expected) if __name__ == '__main__':