Merge pull request #33771 from twangboy/win_dism

Additional functionality to win_dism.py
This commit is contained in:
Mike Place 2016-06-08 06:58:20 -07:00
commit 01aaf3e2a9
5 changed files with 1476 additions and 220 deletions

View file

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
'''
Install features/packages for Windows using DISM, which
is useful for minions not running server versions of
windows
Install features/packages for Windows using DISM, which is useful for minions
not running server versions of Windows. Some functions are only available on
Windows 10.
'''
from __future__ import absolute_import
@ -22,132 +22,368 @@ def __virtual__():
'''
Only work on Windows
'''
if salt.utils.is_windows():
return __virtualname__
return False
if not salt.utils.is_windows():
return False, "Only available on Windows systems"
return __virtualname__
def _install_component(name, component_type, install_name, source=None, limit_access=False):
cmd = 'DISM /Online /{0}-{1} /{1}Name:{2}'.format(install_name, component_type, name)
if source:
cmd += ' /Source:{0}'.format(source)
if limit_access:
cmd += ' /LimitAccess'
return __salt__['cmd.run_all'](cmd)
def _uninstall_component(name, component_type, uninstall_name):
cmd = 'DISM /Online /{0}-{1} /{1}Name:{2}'.format(uninstall_name, component_type, name)
return __salt__['cmd.run_all'](cmd)
def _get_components(type_regex, plural_type, install_value):
cmd = 'DISM /Online /Get-{0}'.format(plural_type)
def _get_components(type_regex, plural_type, install_value, image=None):
cmd = ['DISM',
'/Image:{0}'.format(image) if image else '/Online',
'/Get-{0}'.format(plural_type)]
out = __salt__['cmd.run'](cmd)
pattern = r'{0} : (.*)\r\n.*State : {1}\r\n'.format(type_regex, install_value)
pattern = r'{0} : (.*)\r\n.*State : {1}\r\n'\
.format(type_regex, install_value)
capabilities = re.findall(pattern, out, re.MULTILINE)
capabilities.sort()
return capabilities
def install_capability(capability, source=None, limit_access=False):
def add_capability(capability,
source=None,
limit_access=False,
image=None,
restart=False):
'''
Install a capability
Args:
capability (str): The capability to install
source (Optional[str]): The optional source of the capability. Default
is set by group policy and can be Windows Update.
limit_access (Optional[bool]): Prevent DISM from contacting Windows
Update for the source package
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Raises:
NotImplementedError: For all versions of Windows that are not Windows 10
and later. Server editions of Windows use ServerManager instead.
Returns:
dict: A dictionary containing the results of the command
CLI Example:
.. code-block:: bash
salt '*' dism.install_capability Tools.Graphics.DirectX~~~~0.0.1.0
capability
The capability in which to install
source
The optional source of the capability
limit_access
Prevent DISM from contacting Windows Update for online images
salt '*' dism.add_capability Tools.Graphics.DirectX~~~~0.0.1.0
'''
return _install_component(capability, "Capability", "Add", source, limit_access)
if salt.utils.version_cmp(__grains__['osversion'], '10') == -1:
raise NotImplementedError(
'`install_capability` is not available on this version of Windows: '
'{0}'.format(__grains__['osversion']))
cmd = ['DISM',
'/Quiet',
'/Image:{0}'.format(image) if image else '/Online',
'/Add-Capability',
'/CapabilityName:{0}'.format(capability)]
if source:
cmd.append('/Source:{0}'.format(source))
if limit_access:
cmd.append('/LimitAccess')
if not restart:
cmd.append('/NoRestart')
return __salt__['cmd.run_all'](cmd)
def uninstall_capability(capability):
def remove_capability(capability, image=None, restart=False):
'''
Uninstall a capability
Args:
capability(str): The capability to be removed
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Raises:
NotImplementedError: For all versions of Windows that are not Windows 10
and later. Server editions of Windows use ServerManager instead.
Returns:
dict: A dictionary containing the results of the command
CLI Example:
.. code-block:: bash
salt '*' dism.uninstall_capability Tools.Graphics.DirectX~~~~0.0.1.0
capability
The capability in which to uninstall
salt '*' dism.remove_capability Tools.Graphics.DirectX~~~~0.0.1.0
'''
return _uninstall_component(capability, "Capability", "Remove")
if salt.utils.version_cmp(__grains__['osversion'], '10') == -1:
raise NotImplementedError(
'`uninstall_capability` is not available on this version of '
'Windows: {0}'.format(__grains__['osversion']))
cmd = ['DISM',
'/Quiet',
'/Image:{0}'.format(image) if image else '/Online',
'/Remove-Capability',
'/CapabilityName:{0}'.format(capability)]
if not restart:
cmd.append('/NoRestart')
return __salt__['cmd.run_all'](cmd)
def installed_capabilities():
def get_capabilities(image=None):
'''
List all capabilities on the system
Args:
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
Raises:
NotImplementedError: For all versions of Windows that are not Windows 10
and later. Server editions of Windows use ServerManager instead.
Returns:
list: A list of capabilities
CLI Example:
.. code-block:: bash
salt '*' dism.get_capabilities
'''
if salt.utils.version_cmp(__grains__['osversion'], '10') == -1:
raise NotImplementedError(
'`installed_capabilities` is not available on this version of '
'Windows: {0}'.format(__grains__['osversion']))
cmd = ['DISM',
'/Image:{0}'.format(image) if image else '/Online',
'/Get-Capabilities']
out = __salt__['cmd.run'](cmd)
pattern = r'Capability Identity : (.*)\r\n'
capabilities = re.findall(pattern, out, re.MULTILINE)
capabilities.sort()
return capabilities
def installed_capabilities(image=None):
'''
List the capabilities installed on the system
Args:
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
Raises:
NotImplementedError: For all versions of Windows that are not Windows 10
and later. Server editions of Windows use ServerManager instead.
Returns:
list: A list of installed capabilities
CLI Example:
.. code-block:: bash
salt '*' dism.installed_capabilities
'''
if salt.utils.version_cmp(__grains__['osversion'], '10') == -1:
raise NotImplementedError(
'`installed_capabilities` is not available on this version of '
'Windows: {0}'.format(__grains__['osversion']))
return _get_components("Capability Identity", "Capabilities", "Installed")
def install_feature(capability, source=None, limit_access=False):
def available_capabilities(image=None):
'''
List the capabilities available on the system
Args:
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
Raises:
NotImplementedError: For all versions of Windows that are not Windows 10
and later. Server editions of Windows use ServerManager instead.
Returns:
list: A list of available capabilities
CLI Example:
.. code-block:: bash
salt '*' dism.installed_capabilities
'''
if salt.utils.version_cmp(__grains__['osversion'], '10') == -1:
raise NotImplementedError(
'`installed_capabilities` is not available on this version of '
'Windows: {0}'.format(__grains__['osversion']))
return _get_components("Capability Identity", "Capabilities", "Not Present")
def add_feature(feature,
package=None,
source=None,
limit_access=False,
enable_parent=False,
image=None,
restart=False):
'''
Install a feature using DISM
CLI Example:
Args:
feature (str): The feature to install
package (Optional[str]): The parent package for the feature. You do not
have to specify the package if it is the Windows Foundation Package.
Otherwise, use package to specify the parent package of the feature
source (Optional[str]): The optional source of the capability. Default
is set by group policy and can be Windows Update
limit_access (Optional[bool]): Prevent DISM from contacting Windows
Update for the source package
enable_parent (Optional[bool]): True will enable all parent features of
the specified feature
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
.. code-block:: bash
salt '*' dism.install_feature NetFx3
feature
The feature in which to install
source
The optional source of the feature
limit_access
Prevent DISM from contacting Windows Update for online images
'''
return _install_component(capability, "Feature", "Enable", source, limit_access)
def uninstall_feature(capability):
'''
Uninstall a feature
Returns:
dict: A dictionary containing the results of the command
CLI Example:
.. code-block:: bash
salt '*' dism.uninstall_feature NetFx3
feature
The feature in which to uninstall
salt '*' dism.add_feature NetFx3
'''
return _uninstall_component(capability, "Feature", "Disable")
cmd = ['DISM',
'/Quiet',
'/Image:{0}'.format(image) if image else '/Online',
'/Enable-Feature',
'/FeatureName:{0}'.format(feature)]
if package:
cmd.append('/PackageName:{0}'.format(package))
if source:
cmd.append('/Source:{0}'.format(source))
if limit_access:
cmd.append('/LimitAccess')
if enable_parent:
cmd.append('/All')
if not restart:
cmd.append('/NoRestart')
return __salt__['cmd.run_all'](cmd)
def installed_features():
def remove_feature(feature, remove_payload=False, image=None, restart=False):
'''
Disables the feature.
Args:
feature (str): The feature to uninstall
remove_payload (Optional[bool]): Remove the feature's payload. Must
supply source when enabling in the future.
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Returns:
dict: A dictionary containing the results of the command
CLI Example:
.. code-block:: bash
salt '*' dism.remove_feature NetFx3
'''
cmd = ['DISM',
'/Quiet',
'/Image:{0}'.format(image) if image else '/Online',
'/Disable-Feature',
'/FeatureName:{0}'.format(feature)]
if remove_payload:
cmd.append('/Remove')
if not restart:
cmd.append('/NoRestart')
return __salt__['cmd.run_all'](cmd)
def get_features(package=None, image=None):
'''
List features on the system or in a package
Args:
package (Optional[str]): The full path to the package. Can be either a
.cab file or a folder. Should point to the original source of the
package, not to where the file is installed. You cannot use this
command to get package information for .msu files
This can also be the name of a package as listed in
``dism.installed_packages``
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
Returns:
list: A list of features
CLI Example:
.. code-block:: bash
# Return all features on the system
salt '*' dism.get_features
# Return all features in package.cab
salt '*' dism.get_features C:\\packages\\package.cab
# Return all features in the calc package
salt '*' dism.get_features Microsoft.Windows.Calc.Demo~6595b6144ccf1df~x86~en~1.0.0.0
'''
cmd = ['DISM',
'/Image:{0}'.format(image) if image else '/Online',
'/Get-Features']
if package:
if '~' in package:
cmd.append('/PackageName:{0}'.format(package))
else:
cmd.append('/PackagePath:{0}'.format(package))
out = __salt__['cmd.run'](cmd)
pattern = r'Feature Name : (.*)\r\n'
features = re.findall(pattern, out, re.MULTILINE)
features.sort()
return features
def installed_features(image=None):
'''
List the features installed on the system
Args:
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
Returns:
list: A list of installed features
CLI Example:
.. code-block:: bash
@ -155,3 +391,180 @@ def installed_features():
salt '*' dism.installed_features
'''
return _get_components("Feature Name", "Features", "Enabled")
def available_features(image=None):
'''
List the features available on the system
Args:
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
Returns:
list: A list of available features
CLI Example:
.. code-block:: bash
salt '*' dism.available_features
'''
return _get_components("Feature Name", "Features", "Disabled")
def add_package(package,
ignore_check=False,
prevent_pending=False,
image=None,
restart=False):
'''
Install a package using DISM
Args:
package (str): The package to install. Can be a .cab file, a .msu file,
or a folder
ignore_check (Optional[bool]): Skip installation of the package if the
applicability checks fail
prevent_pending (Optional[bool]): Skip the installation of the package
if there are pending online actions
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Returns:
dict: A dictionary containing the results of the command
CLI Example:
.. code-block:: bash
salt '*' dism.add_package C:\\Packages\\package.cab
'''
cmd = ['DISM',
'/Quiet',
'/Image:{0}'.format(image) if image else '/Online',
'/Add-Package',
'/PackagePath:{0}'.format(package)]
if ignore_check:
cmd.append('/IgnoreCheck')
if prevent_pending:
cmd.append('/PreventPending')
if not restart:
cmd.append('/NoRestart')
return __salt__['cmd.run_all'](cmd)
def remove_package(package, image=None, restart=False):
'''
Uninstall a package
Args:
package (str): The full path to the package. Can be either a .cab file
or a folder. Should point to the original source of the package, not
to where the file is installed. This can also be the name of a package as listed in
``dism.installed_packages``
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Returns:
dict: A dictionary containing the results of the command
CLI Example:
.. code-block:: bash
# Remove the Calc Package
salt '*' dism.remove_package Microsoft.Windows.Calc.Demo~6595b6144ccf1df~x86~en~1.0.0.0
# Remove the package.cab (does not remove C:\\packages\\package.cab)
salt '*' dism.remove_package C:\\packages\\package.cab
'''
cmd = ['DISM',
'/Quiet',
'/Image:{0}'.format(image) if image else '/Online',
'/Remove-Package']
if not restart:
cmd.append('/NoRestart')
if '~' in package:
cmd.append('/PackageName:{0}'.format(package))
else:
cmd.append('/PackagePath:{0}'.format(package))
return __salt__['cmd.run_all'](cmd)
def installed_packages(image=None):
'''
List the packages installed on the system
Args:
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
Returns:
list: A list of installed packages
CLI Example:
.. code-block:: bash
salt '*' dism.installed_packages
'''
return _get_components("Package Identity", "Packages", "Installed")
def package_info(package, image=None):
'''
Display information about a package
Args:
package (str): The full path to the package. Can be either a .cab file
or a folder. Should point to the original source of the package, not
to where the file is installed. You cannot use this command to get
package information for .msu files
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
Returns:
dict: A dictionary containing the results of the command
CLI Example:
.. code-block:: bash
salt '*' dism. package_info C:\\packages\\package.cab
'''
cmd = ['DISM',
'/Image:{0}'.format(image) if image else '/Online',
'/Get-PackageInfo']
if '~' in package:
cmd.append('/PackageName:{0}'.format(package))
else:
cmd.append('/PackagePath:{0}'.format(package))
out = __salt__['cmd.run_all'](cmd)
if out['retcode'] == 0:
ret = dict()
for line in str(out['stdout']).splitlines():
if ' : ' in line:
info = line.split(' : ')
if len(info) < 2:
continue
ret[info[0]] = info[1]
else:
ret = out
return ret

View file

@ -27,84 +27,397 @@ __virtualname__ = "dism"
def __virtual__():
'''
Only work on Windows
Only work on Windows where the DISM module is available
'''
if salt.utils.is_windows() and 'dism.install_capability' in __salt__:
return __virtualname__
return False
if not salt.utils.is_windows():
return False, 'Module only available on Windows'
return __virtualname__
def capability_installed(name, source=None, limit_access=False):
def capability_installed(name,
source=None,
limit_access=False,
image=None,
restart=False):
'''
Install a DISM capability
name
The capability in which to install
Args:
name (str): The capability to install
source (str): The optional source of the capability
limit_access (bool): Prevent DISM from contacting Windows Update for
online images
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
source
The optional source of the capability
Example:
Run ``dism.available_capabilities`` to get a list of available
capabilities. This will help you get the proper name to use.
limit_access
Prevent DISM from contacting Windows Update for online images
.. code-block:: yaml
install_dotnet35:
dism.capability_installed:
- name: NetFX3~~~~
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}}
comment = []
old = __salt__['dism.installed_capabilities']()
installed_capabilities = __salt__['dism.installed_capabilities']()
if name in old:
ret['comment'] = 'The capability {0} is already installed'.format(name)
return ret
if name in installed_capabilities:
comment.append("{0} was already installed.\n".format(name))
else:
out = __salt__['dism.install_capability'](name, source, limit_access)
if out['retcode'] == 0:
comment.append("{0} was installed.\n".format(name))
ret['changes']['installed'] = name
else:
comment.append("{0} was unable to be installed. {1}\n".format(name, out['stdout']))
ret['result'] = False
if __opts__['test']:
ret['changes']['capability'] = '{0} will be installed'.format(name)
ret['result'] = None
return ret
# Install the capability
status = __salt__['dism.add_capability'](
name, source, limit_access, image, restart)
if status['retcode'] != 0:
ret['comment'] = 'Failed to install {0}: {1}'\
.format(name, status['stdout'])
ret['result'] = False
new = __salt__['dism.installed_capabilities']()
changes = salt.utils.compare_lists(old, new)
if changes:
ret['comment'] = 'Installed {0}'.format(name)
ret['changes'] = status
ret['changes']['capability'] = changes
ret['comment'] = ' '.join(comment)
return ret
def feature_installed(name, source=None, limit_access=False):
def capability_removed(name, image=None, restart=False):
'''
Uninstall a DISM capability
Args:
name (str): The capability to uninstall
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Example:
Run ``dism.installed_capabilities`` to get a list of installed
capabilities. This will help you get the proper name to use.
.. code-block:: yaml
remove_dotnet35:
dism.capability_removed:
- name: NetFX3~~~~
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}}
old = __salt__['dism.installed_capabilities']()
if name not in old:
ret['comment'] = 'The capability {0} is already removed'.format(name)
return ret
if __opts__['test']:
ret['changes']['capability'] = '{0} will be removed'.format(name)
ret['result'] = None
return ret
# Remove the capability
status = __salt__['dism.remove_capability'](name, image, restart)
if status['retcode'] != 0:
ret['comment'] = 'Failed to remove {0}: {1}' \
.format(name, status['stdout'])
ret['result'] = False
new = __salt__['dism.installed_capabilities']()
changes = salt.utils.compare_lists(old, new)
if changes:
ret['comment'] = 'Removed {0}'.format(name)
ret['changes'] = status
ret['changes']['capability'] = changes
return ret
def feature_installed(name,
package=None,
source=None,
limit_access=False,
enable_parent=False,
image=None,
restart=False):
'''
Install a DISM feature
name
The feature in which to install
Args:
name (str): The feature in which to install
package (Optional[str]): The parent package for the feature. You do not
have to specify the package if it is the Windows Foundation Package.
Otherwise, use package to specify the parent package of the feature
source (str): The optional source of the feature
limit_access (bool): Prevent DISM from contacting Windows Update for
online images
enable_parent (Optional[bool]): True will enable all parent features of
the specified feature
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
source
The optional source of the feature
Example:
Run ``dism.available_features`` to get a list of available features.
This will help you get the proper name to use.
limit_access
Prevent DISM from contacting Windows Update for online images
.. code-block:: yaml
install_telnet_client:
dism.feature_installed:
- name: TelnetClient
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}}
comment = []
old = __salt__['dism.installed_features']()
installed_features = __salt__['dism.installed_features']()
if name in old:
ret['comment'] = 'The feature {0} is already installed'.format(name)
return ret
if name in installed_features:
comment.append("{0} was already installed.\n".format(name))
else:
out = __salt__['dism.install_feature'](name, source, limit_access)
if out['retcode'] == 0:
comment.append("{0} was installed.\n".format(name))
ret['changes']['installed'] = name
else:
comment.append("{0} was unable to be installed. {1}\n".format(name, out['stdout']))
ret['result'] = False
if __opts__['test']:
ret['changes']['feature'] = '{0} will be installed'.format(name)
ret['result'] = None
return ret
# Install the feature
status = __salt__['dism.add_feature'](
name, package, source, limit_access, enable_parent, image, restart)
if status['retcode'] != 0:
ret['comment'] = 'Failed to install {0}: {1}' \
.format(name, status['stdout'])
ret['result'] = False
new = __salt__['dism.installed_features']()
changes = salt.utils.compare_lists(old, new)
if changes:
ret['comment'] = 'Installed {0}'.format(name)
ret['changes'] = status
ret['changes']['feature'] = changes
return ret
def feature_removed(name, remove_payload=False, image=None, restart=False):
'''
Disables a feature.
Args:
name (str): The feature to disable
remove_payload (Optional[bool]): Remove the feature's payload. Must
supply source when enabling in the future.
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Example:
Run ``dism.installed_features`` to get a list of installed features.
This will help you get the proper name to use.
.. code-block:: yaml
remove_telnet_client:
dism.feature_removed:
- name: TelnetClient
- remove_payload: True
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}}
old = __salt__['dism.installed_features']()
if name not in old:
ret['comment'] = 'The feature {0} is already removed'.format(name)
return ret
if __opts__['test']:
ret['changes']['feature'] = '{0} will be removed'.format(name)
ret['result'] = None
return ret
# Remove the feature
status = __salt__['dism.remove_feature'](
name, remove_payload, image, restart)
if status['retcode'] != 0:
ret['comment'] = 'Failed to remove {0}: {1}' \
.format(name, status['stdout'])
ret['result'] = False
new = __salt__['dism.installed_features']()
changes = salt.utils.compare_lists(old, new)
if changes:
ret['comment'] = 'Removed {0}'.format(name)
ret['changes'] = status
ret['changes']['feature'] = changes
return ret
def package_installed(name,
ignore_check=False,
prevent_pending=False,
image=None,
restart=False):
'''
Install a package.
Args:
name (str): The package to install. Can be a .cab file, a .msu file,
or a folder
ignore_check (Optional[bool]): Skip installation of the package if the
applicability checks fail
prevent_pending (Optional[bool]): Skip the installation of the package
if there are pending online actions
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Example:
.. code-block:: yaml
install_KB123123123:
dism.package_installed:
- name: C:\\Packages\\KB123123123.cab
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}}
old = __salt__['dism.installed_packages']()
# Get package info so we can see if it's already installed
package_info = __salt__['dism.package_info'](name)
if package_info['Package Identity'] in old:
ret['comment'] = 'The package {0} is already installed: {1}'\
.format(name, package_info['Package Identity'])
return ret
if __opts__['test']:
ret['changes']['package'] = '{0} will be installed'.format(name)
ret['result'] = None
return ret
# Install the package
status = __salt__['dism.add_package'](
name, ignore_check, prevent_pending, image, restart)
if status['retcode'] != 0:
ret['comment'] = 'Failed to install {0}: {1}' \
.format(name, status['stdout'])
ret['result'] = False
new = __salt__['dism.installed_packages']()
changes = salt.utils.compare_lists(old, new)
if changes:
ret['comment'] = 'Installed {0}'.format(name)
ret['changes'] = status
ret['changes']['package'] = changes
return ret
def package_removed(name, image=None, restart=False):
'''
Uninstall a package
Args:
name (str): The full path to the package. Can be either a .cab file or a
folder. Should point to the original source of the package, not to
where the file is installed. This can also be the name of a package as listed in
``dism.installed_packages``
image (Optional[str]): The path to the root directory of an offline
Windows image. If `None` is passed, the running operating system is
targeted. Default is None.
restart (Optional[bool]): Reboot the machine if required by the install
Example:
.. code-block:: yaml
# Example using source
remove_KB1231231:
dism.package_installed:
- name: C:\\Packages\\KB1231231.cab
# Example using name from ``dism.installed_packages``
remove_KB1231231:
dism.package_installed:
- name: Package_for_KB1231231~31bf3856ad364e35~amd64~~10.0.1.3
'''
ret = {'name': name,
'result': True,
'comment': '',
'changes': {}}
old = __salt__['dism.installed_packages']()
# Get package info so we can see if it's already removed
package_info = __salt__['dism.package_info'](name)
# If `Package Identity` isn't returned or if they passed a cab file, if
# `Package Identity` isn't in the list of installed packages
if 'Package Identity' not in package_info or \
package_info['Package Identity'] not in old:
ret['comment'] = 'The package {0} is already removed'.format(name)
return ret
if __opts__['test']:
ret['changes']['package'] = '{0} will be removed'.format(name)
ret['result'] = None
return ret
# Remove the package
status = __salt__['dism.remove_package'](name, image, restart)
if status['retcode'] != 0:
ret['comment'] = 'Failed to remove {0}: {1}' \
.format(name, status['stdout'])
ret['result'] = False
new = __salt__['dism.installed_packages']()
changes = salt.utils.compare_lists(old, new)
if changes:
ret['comment'] = 'Removed {0}'.format(name)
ret['changes'] = status
ret['changes']['package'] = changes
ret['comment'] = ' '.join(comment)
return ret

View file

@ -2363,6 +2363,21 @@ def compare_dicts(old=None, new=None):
return ret
def compare_lists(old=None, new=None):
'''
Compare before and after results from various salt functions, returning a
dict describing the changes that were made
'''
ret = dict()
for item in new:
if item not in old:
ret['new'] = item
for item in old:
if item not in new:
ret['old'] = item
return ret
def argspec_report(functions, module=''):
'''
Pass in a functions dict as it is returned from the loader and return the

View file

@ -17,36 +17,62 @@ from salttesting.mock import (
ensure_in_syspath('../../')
dism.__salt__ = {}
dism.__grains__ = {}
class DISMTestCase(TestCase):
class WinDismTestCase(TestCase):
def test_install_capability(self):
def test_add_capability(self):
'''
Test installing a capability with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.install_capability("test")
mock.assert_called_once_with('DISM /Online /Add-Capability /CapabilityName:test')
with patch.dict(dism.__grains__, {'osversion': 10}):
dism.add_capability("test")
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Add-Capability',
'/CapabilityName:test', '/NoRestart'])
def test_install_capability_with_extras(self):
def test_add_capability_with_extras(self):
'''
Test installing a capability with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.install_capability("test", "life", True)
mock.assert_called_once_with('DISM /Online /Add-Capability /CapabilityName:test /Source:life /LimitAccess')
with patch.dict(dism.__grains__, {'osversion': 10}):
dism.add_capability("test", "life", True)
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Add-Capability',
'/CapabilityName:test', '/Source:life', '/LimitAccess',
'/NoRestart'])
def test_uninstall_capability(self):
def test_remove_capability(self):
'''
Test uninstalling a capability with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.uninstall_capability("test")
mock.assert_called_once_with('DISM /Online /Remove-Capability /CapabilityName:test')
with patch.dict(dism.__grains__, {'osversion': 10}):
dism.remove_capability("test")
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Remove-Capability',
'/CapabilityName:test', '/NoRestart'])
def test_get_capabilities(self):
'''
Test getting all the capabilities
'''
capabilties = "Capability Identity : Capa1\r\n State : Installed\r\n" \
"Capability Identity : Capa2\r\n State : Disabled\r\n"
mock = MagicMock(return_value=capabilties)
with patch.dict(dism.__salt__, {'cmd.run': mock}):
with patch.dict(dism.__grains__, {'osversion': 10}):
out = dism.get_capabilities()
mock.assert_called_once_with(
['DISM', '/Online', '/Get-Capabilities'])
self.assertEqual(out, ['Capa1', 'Capa2'])
def test_installed_capabilities(self):
'''
@ -57,41 +83,158 @@ class DISMTestCase(TestCase):
mock = MagicMock(return_value=capabilties)
with patch.dict(dism.__salt__, {'cmd.run': mock}):
out = dism.installed_capabilities()
mock.assert_called_once_with('DISM /Online /Get-Capabilities')
self.assertEqual(out, ["Capa1"])
with patch.dict(dism.__grains__, {'osversion': 10}):
out = dism.installed_capabilities()
mock.assert_called_once_with(
['DISM', '/Online', '/Get-Capabilities'])
self.assertEqual(out, ["Capa1"])
def test_install_feature(self):
def test_available_capabilities(self):
'''
Test getting all the available capabilities
'''
capabilties = "Capability Identity : Capa1\r\n State : Installed\r\n" \
"Capability Identity : Capa2\r\n State : Not Present\r\n"
mock = MagicMock(return_value=capabilties)
with patch.dict(dism.__salt__, {'cmd.run': mock}):
with patch.dict(dism.__grains__, {'osversion': 10}):
out = dism.available_capabilities()
mock.assert_called_once_with(
['DISM', '/Online', '/Get-Capabilities'])
self.assertEqual(out, ["Capa2"])
def test_add_feature(self):
'''
Test installing a feature with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.install_feature("test")
mock.assert_called_once_with('DISM /Online /Enable-Feature /FeatureName:test')
dism.add_feature("test")
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Enable-Feature',
'/FeatureName:test', '/NoRestart'])
def test_uninstall_feature(self):
def test_add_feature_with_extras(self):
'''
Test installing a feature with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.add_feature('sponge', 'bob', 'C:\\temp', True, True)
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Enable-Feature',
'/FeatureName:sponge', '/PackageName:bob', '/Source:C:\\temp',
'/LimitAccess', '/All', '/NoRestart'])
def test_remove_feature(self):
'''
Test uninstalling a capability with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.uninstall_feature("test")
mock.assert_called_once_with('DISM /Online /Disable-Feature /FeatureName:test')
dism.remove_feature("test")
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Disable-Feature',
'/FeatureName:test', '/NoRestart'])
def test_installed_feature(self):
def test_remove_feature_with_extras(self):
'''
Test getting all the installed capabilities
Test uninstalling a capability with DISM
'''
capabilties = "Feature Name : Capa1\r\n State : Enabled\r\n" \
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.remove_feature('sponge', True)
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Disable-Feature',
'/FeatureName:sponge', '/Remove', '/NoRestart'])
def test_get_features(self):
'''
Test getting all the features
'''
features = "Feature Name : Capa1\r\n State : Enabled\r\n" \
"Feature Name : Capa2\r\n State : Disabled\r\n"
mock = MagicMock(return_value=capabilties)
mock = MagicMock(return_value=features)
with patch.dict(dism.__salt__, {'cmd.run': mock}):
out = dism.get_features()
mock.assert_called_once_with(['DISM', '/Online', '/Get-Features'])
self.assertEqual(out, ['Capa1', 'Capa2'])
def test_installed_features(self):
'''
Test getting all the installed features
'''
features = "Feature Name : Capa1\r\n State : Enabled\r\n" \
"Feature Name : Capa2\r\n State : Disabled\r\n"
mock = MagicMock(return_value=features)
with patch.dict(dism.__salt__, {'cmd.run': mock}):
out = dism.installed_features()
mock.assert_called_once_with('DISM /Online /Get-Features')
mock.assert_called_once_with(['DISM', '/Online', '/Get-Features'])
self.assertEqual(out, ["Capa1"])
def test_available_features(self):
'''
Test getting all the available features
'''
features = "Feature Name : Capa1\r\n State : Enabled\r\n" \
"Feature Name : Capa2\r\n State : Disabled\r\n"
mock = MagicMock(return_value=features)
with patch.dict(dism.__salt__, {'cmd.run': mock}):
out = dism.available_features()
mock.assert_called_once_with(['DISM', '/Online', '/Get-Features'])
self.assertEqual(out, ["Capa2"])
def test_add_package(self):
'''
Test installing a package with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.add_package("test")
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Add-Package',
'/PackagePath:test', '/NoRestart'])
def test_add_package_with_extras(self):
'''
Test installing a package with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.add_package('sponge', True, True)
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Add-Package',
'/PackagePath:sponge', '/IgnoreCheck', '/PreventPending',
'/NoRestart'])
def test_remove_package(self):
'''
Test uninstalling a package with DISM
'''
mock = MagicMock()
with patch.dict(dism.__salt__, {'cmd.run_all': mock}):
dism.remove_package("test")
mock.assert_called_once_with(
['DISM', '/Quiet', '/Online', '/Remove-Package', '/NoRestart',
'/PackagePath:test'])
def test_installed_packages(self):
'''
Test getting all the installed features
'''
features = "Package Identity : Capa1\r\n State : Installed\r\n" \
"Package Identity : Capa2\r\n State : Installed\r\n"
mock = MagicMock(return_value=features)
with patch.dict(dism.__salt__, {'cmd.run': mock}):
out = dism.installed_packages()
mock.assert_called_once_with(['DISM', '/Online', '/Get-Packages'])
self.assertEqual(out, ['Capa1', 'Capa2'])
if __name__ == '__main__':
from integration import run_tests
run_tests(DISMTestCase, needs_daemon=False)
run_tests(WinDismTestCase, needs_daemon=False)

View file

@ -17,131 +17,503 @@ from salttesting.mock import (
ensure_in_syspath('../../')
dism.__salt__ = {}
dism.__opts__ = {}
class DISMTestCase(TestCase):
class WinDismTestCase(TestCase):
def test_install_capability(self):
def test_capability_installed(self):
'''
Test installing a capability with DISM
Test capability installed state
'''
expected = {
'comment': "Capa2 was installed.\n",
'changes': {'installed': 'Capa2'},
'comment': "Installed Capa2",
'changes': {'capability': {'new': 'Capa2'},
'retcode': 0},
'name': 'Capa2',
'result': True
}
'result': True}
installed_mock = MagicMock(return_value=["Capa1"])
install_mock = MagicMock(return_value={'retcode': 0})
with patch.dict(dism.__salt__, {'dism.installed_capabilities': installed_mock,
'dism.install_capability': install_mock}):
out = dism.capability_installed('Capa2', 'somewhere', True)
installed_mock.assert_called_once_with()
install_mock.assert_called_once_with('Capa2', 'somewhere', True)
self.assertEqual(out, expected)
mock_installed = MagicMock(
side_effect=[['Capa1'], ['Capa1', 'Capa2']])
mock_add = MagicMock(
return_value={'retcode': 0})
def test_install_capability_failure(self):
with patch.dict(
dism.__salt__, {'dism.installed_capabilities': mock_installed,
'dism.add_capability': mock_add}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.capability_installed('Capa2', 'somewhere', True)
mock_installed.assert_called_with()
mock_add.assert_called_once_with(
'Capa2', 'somewhere', True, None, False)
self.assertEqual(out, expected)
def test_capability_installed_failure(self):
'''
Test installing a capability which fails with DISM
'''
expected = {
'comment': "Capa2 was unable to be installed. Failed\n",
'comment': "Failed to install Capa2: Failed",
'changes': {},
'name': 'Capa2',
'result': False
}
'result': False}
installed_mock = MagicMock(return_value=["Capa1"])
install_mock = MagicMock(return_value={'retcode': 67, 'stdout': 'Failed'})
with patch.dict(dism.__salt__, {'dism.installed_capabilities': installed_mock,
'dism.install_capability': install_mock}):
out = dism.capability_installed('Capa2', 'somewhere', True)
installed_mock.assert_called_once_with()
install_mock.assert_called_once_with('Capa2', 'somewhere', True)
self.assertEqual(out, expected)
mock_installed = MagicMock(
side_effect=[['Capa1'], ['Capa1']])
mock_add = MagicMock(
return_value={'retcode': 67, 'stdout': 'Failed'})
def test_installed_capability(self):
with patch.dict(
dism.__salt__, {'dism.installed_capabilities': mock_installed,
'dism.add_capability': mock_add}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.capability_installed('Capa2', 'somewhere', True)
mock_installed.assert_called_with()
mock_add.assert_called_once_with(
'Capa2', 'somewhere', True, None, False)
self.assertEqual(out, expected)
def test_capability_installed_installed(self):
'''
Test installing a capability already installed
'''
expected = {
'comment': "Capa2 was already installed.\n",
'comment': "The capability Capa2 is already installed",
'changes': {},
'name': 'Capa2',
'result': True
}
'result': True}
mock_installed = MagicMock(
return_value=["Capa1", "Capa2"])
mock_add = MagicMock()
with patch.dict(
dism.__salt__, {'dism.installed_capabilities': mock_installed,
'dism.add_capability': mock_add}):
installed_mock = MagicMock(return_value=["Capa1", "Capa2"])
install_mock = MagicMock()
with patch.dict(dism.__salt__, {'dism.installed_capabilities': installed_mock,
'dism.install_capability': install_mock}):
out = dism.capability_installed('Capa2', 'somewhere', True)
installed_mock.assert_called_once_with()
assert not install_mock.called
mock_installed.assert_called_once_with()
assert not mock_add.called
self.assertEqual(out, expected)
def test_install_feature(self):
def test_capability_removed(self):
'''
Test capability removed state
'''
expected = {
'comment': "Removed Capa2",
'changes': {'capability': {'old': 'Capa2'},
'retcode': 0},
'name': 'Capa2',
'result': True}
mock_removed = MagicMock(
side_effect=[['Capa1', 'Capa2'], ['Capa1']])
mock_remove = MagicMock(
return_value={'retcode': 0})
with patch.dict(
dism.__salt__, {'dism.installed_capabilities': mock_removed,
'dism.remove_capability': mock_remove}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.capability_removed('Capa2')
mock_removed.assert_called_with()
mock_remove.assert_called_once_with('Capa2', None, False)
self.assertEqual(out, expected)
def test_capability_removed_failure(self):
'''
Test removing a capability which fails with DISM
'''
expected = {
'comment': "Failed to remove Capa2: Failed",
'changes': {},
'name': 'Capa2',
'result': False}
mock_removed = MagicMock(
side_effect=[['Capa1', 'Capa2'], ['Capa1', 'Capa2']])
mock_remove = MagicMock(
return_value={'retcode': 67, 'stdout': 'Failed'})
with patch.dict(
dism.__salt__, {'dism.installed_capabilities': mock_removed,
'dism.remove_capability': mock_remove}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.capability_removed('Capa2')
mock_removed.assert_called_with()
mock_remove.assert_called_once_with(
'Capa2', None, False)
self.assertEqual(out, expected)
def test_capability_removed_removed(self):
'''
Test removing a capability already removed
'''
expected = {
'comment': "The capability Capa2 is already removed",
'changes': {},
'name': 'Capa2',
'result': True}
mock_removed = MagicMock(
return_value=["Capa1"])
mock_remove = MagicMock()
with patch.dict(
dism.__salt__, {'dism.installed_capabilities': mock_removed,
'dism.add_capability': mock_remove}):
out = dism.capability_removed('Capa2', 'somewhere', True)
mock_removed.assert_called_once_with()
assert not mock_remove.called
self.assertEqual(out, expected)
def test_feature_installed(self):
'''
Test installing a feature with DISM
'''
expected = {
'comment': "Feat1 was installed.\n",
'changes': {'installed': 'Feat1'},
'name': 'Feat1',
'result': True
}
'comment': "Installed Feat2",
'changes': {'feature': {'new': 'Feat2'},
'retcode': 0},
'name': 'Feat2',
'result': True}
installed_mock = MagicMock(return_value=["Feat2"])
install_mock = MagicMock(return_value={'retcode': 0})
with patch.dict(dism.__salt__, {'dism.installed_features': installed_mock,
'dism.install_feature': install_mock}):
out = dism.feature_installed('Feat1', 'somewhere', True)
installed_mock.assert_called_once_with()
install_mock.assert_called_once_with('Feat1', 'somewhere', True)
self.assertEqual(out, expected)
mock_installed = MagicMock(
side_effect=[['Feat1'], ['Feat1', 'Feat2']])
mock_add = MagicMock(
return_value={'retcode': 0})
def test_install_feature_failure(self):
with patch.dict(
dism.__salt__, {'dism.installed_features': mock_installed,
'dism.add_feature': mock_add}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.feature_installed('Feat2')
mock_installed.assert_called_with()
mock_add.assert_called_once_with(
'Feat2', None, None, False, False, None, False)
self.assertEqual(out, expected)
def test_feature_installed_failure(self):
'''
Test installing a feature which fails with DISM
'''
expected = {
'comment': "Feat1 was unable to be installed. Failed\n",
'comment': "Failed to install Feat2: Failed",
'changes': {},
'name': 'Feat1',
'result': False
}
'name': 'Feat2',
'result': False}
installed_mock = MagicMock(return_value=["Feat3"])
install_mock = MagicMock(return_value={'retcode': 67, 'stdout': 'Failed'})
with patch.dict(dism.__salt__, {'dism.installed_features': installed_mock,
'dism.install_feature': install_mock}):
out = dism.feature_installed('Feat1', 'somewhere', True)
installed_mock.assert_called_once_with()
install_mock.assert_called_once_with('Feat1', 'somewhere', True)
self.assertEqual(out, expected)
mock_installed = MagicMock(
side_effect=[['Feat1'], ['Feat1']])
mock_add = MagicMock(
return_value={'retcode': 67, 'stdout': 'Failed'})
def test_installed_feature(self):
with patch.dict(
dism.__salt__, {'dism.installed_features': mock_installed,
'dism.add_feature': mock_add}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.feature_installed('Feat2')
mock_installed.assert_called_with()
mock_add.assert_called_once_with(
'Feat2', None, None, False, False, None, False)
self.assertEqual(out, expected)
def test_feature_installed_installed(self):
'''
Test installing a feature already installed
'''
expected = {
'comment': "Feat1 was already installed.\n",
'comment': "The feature Feat1 is already installed",
'changes': {},
'name': 'Feat1',
'result': True
}
'result': True}
installed_mock = MagicMock(return_value=["Feat1", "Feat2"])
install_mock = MagicMock()
with patch.dict(dism.__salt__, {'dism.installed_features': installed_mock,
'dism.install_feature': install_mock}):
out = dism.feature_installed('Feat1', 'somewhere', True)
installed_mock.assert_called_once_with()
assert not install_mock.called
mock_installed = MagicMock(
side_effect=[['Feat1', 'Feat2'], ['Feat1', 'Feat2']])
mock_add = MagicMock()
with patch.dict(
dism.__salt__, {'dism.installed_features': mock_installed,
'dism.add_feature': mock_add}):
out = dism.feature_installed('Feat1')
mock_installed.assert_called_once_with()
assert not mock_add.called
self.assertEqual(out, expected)
def test_feature_removed(self):
'''
Test removing a feature with DISM
'''
expected = {
'comment': "Removed Feat2",
'changes': {'feature': {'old': 'Feat2'},
'retcode': 0},
'name': 'Feat2',
'result': True}
mock_removed = MagicMock(
side_effect=[['Feat1', 'Feat2'], ['Feat1']])
mock_remove = MagicMock(
return_value={'retcode': 0})
with patch.dict(
dism.__salt__, {'dism.installed_features': mock_removed,
'dism.remove_feature': mock_remove}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.feature_removed('Feat2')
mock_removed.assert_called_with()
mock_remove.assert_called_once_with(
'Feat2', False, None, False)
self.assertEqual(out, expected)
def test_feature_removed_failure(self):
'''
Test removing a feature which fails with DISM
'''
expected = {
'comment': "Failed to remove Feat2: Failed",
'changes': {},
'name': 'Feat2',
'result': False}
mock_removed = MagicMock(
side_effect=[['Feat1', 'Feat2'], ['Feat1', 'Feat2']])
mock_remove = MagicMock(
return_value={'retcode': 67, 'stdout': 'Failed'})
with patch.dict(
dism.__salt__, {'dism.installed_features': mock_removed,
'dism.remove_feature': mock_remove}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.feature_removed('Feat2')
mock_removed.assert_called_with()
mock_remove.assert_called_once_with(
'Feat2', False, None, False)
self.assertEqual(out, expected)
def test_feature_removed_removed(self):
'''
Test removing a feature already removed
'''
expected = {
'comment': "The feature Feat2 is already removed",
'changes': {},
'name': 'Feat2',
'result': True}
mock_removed = MagicMock(
side_effect=[['Feat1'], ['Feat1']])
mock_remove = MagicMock()
with patch.dict(
dism.__salt__, {'dism.installed_features': mock_removed,
'dism.remove_feature': mock_remove}):
out = dism.feature_removed('Feat2')
mock_removed.assert_called_once_with()
assert not mock_remove.called
self.assertEqual(out, expected)
def test_package_installed(self):
'''
Test installing a package with DISM
'''
expected = {
'comment': "Installed Pack2",
'changes': {'package': {'new': 'Pack2'},
'retcode': 0},
'name': 'Pack2',
'result': True}
mock_installed = MagicMock(
side_effect=[['Pack1'], ['Pack1', 'Pack2']])
mock_add = MagicMock(
return_value={'retcode': 0})
mock_info = MagicMock(
return_value={'Package Identity': 'Pack2'})
with patch.dict(
dism.__salt__, {'dism.installed_packages': mock_installed,
'dism.add_package': mock_add,
'dism.package_info': mock_info}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.package_installed('Pack2')
mock_installed.assert_called_with()
mock_add.assert_called_once_with(
'Pack2', False, False, None, False)
self.assertEqual(out, expected)
def test_package_installed_failure(self):
'''
Test installing a package which fails with DISM
'''
expected = {
'comment': "Failed to install Pack2: Failed",
'changes': {},
'name': 'Pack2',
'result': False}
mock_installed = MagicMock(
side_effect=[['Pack1'], ['Pack1']])
mock_add = MagicMock(
return_value={'retcode': 67, 'stdout': 'Failed'})
mock_info = MagicMock(
return_value={'Package Identity': 'Pack2'})
with patch.dict(
dism.__salt__, {'dism.installed_packages': mock_installed,
'dism.add_package': mock_add,
'dism.package_info': mock_info}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.package_installed('Pack2')
mock_installed.assert_called_with()
mock_add.assert_called_once_with(
'Pack2', False, False, None, False)
self.assertEqual(out, expected)
def test_package_installed_installed(self):
'''
Test installing a package already installed
'''
expected = {
'comment': "The package Pack2 is already installed: Pack2",
'changes': {},
'name': 'Pack2',
'result': True}
mock_installed = MagicMock(
side_effect=[['Pack1', 'Pack2'], ['Pack1', 'Pack2']])
mock_add = MagicMock()
mock_info = MagicMock(
return_value={'Package Identity': 'Pack2'})
with patch.dict(
dism.__salt__, {'dism.installed_packages': mock_installed,
'dism.add_package': mock_add,
'dism.package_info': mock_info}):
out = dism.package_installed('Pack2')
mock_installed.assert_called_once_with()
assert not mock_add.called
self.assertEqual(out, expected)
def test_package_removed(self):
'''
Test removing a package with DISM
'''
expected = {
'comment': "Removed Pack2",
'changes': {'package': {'old': 'Pack2'},
'retcode': 0},
'name': 'Pack2',
'result': True}
mock_removed = MagicMock(
side_effect=[['Pack1', 'Pack2'], ['Pack1']])
mock_remove = MagicMock(
return_value={'retcode': 0})
mock_info = MagicMock(
return_value={'Package Identity': 'Pack2'})
with patch.dict(
dism.__salt__, {'dism.installed_packages': mock_removed,
'dism.remove_package': mock_remove,
'dism.package_info': mock_info}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.package_removed('Pack2')
mock_removed.assert_called_with()
mock_remove.assert_called_once_with(
'Pack2', None, False)
self.assertEqual(out, expected)
def test_package_removed_failure(self):
'''
Test removing a package which fails with DISM
'''
expected = {
'comment': "Failed to remove Pack2: Failed",
'changes': {},
'name': 'Pack2',
'result': False}
mock_removed = MagicMock(
side_effect=[['Pack1', 'Pack2'], ['Pack1', 'Pack2']])
mock_remove = MagicMock(
return_value={'retcode': 67, 'stdout': 'Failed'})
mock_info = MagicMock(
return_value={'Package Identity': 'Pack2'})
with patch.dict(
dism.__salt__, {'dism.installed_packages': mock_removed,
'dism.remove_package': mock_remove,
'dism.package_info': mock_info}):
with patch.dict(dism.__opts__, {'test': False}):
out = dism.package_removed('Pack2')
mock_removed.assert_called_with()
mock_remove.assert_called_once_with(
'Pack2', None, False)
self.assertEqual(out, expected)
def test_package_removed_removed(self):
'''
Test removing a package already removed
'''
expected = {
'comment': "The package Pack2 is already removed",
'changes': {},
'name': 'Pack2',
'result': True}
mock_removed = MagicMock(
side_effect=[['Pack1'], ['Pack1']])
mock_remove = MagicMock()
mock_info = MagicMock(
return_value={'Package Identity': 'Pack2'})
with patch.dict(
dism.__salt__, {'dism.installed_packages': mock_removed,
'dism.remove_package': mock_remove,
'dism.package_info': mock_info}):
out = dism.package_removed('Pack2')
mock_removed.assert_called_once_with()
assert not mock_remove.called
self.assertEqual(out, expected)
if __name__ == '__main__':
from integration import run_tests
run_tests(DISMTestCase, needs_daemon=False)
run_tests(WinDismTestCase, needs_daemon=False)