Merge branch 'carbon' into '2016.11'

No conflicts.
This commit is contained in:
rallytime 2016-11-04 13:38:34 -06:00
commit 4132929832
9 changed files with 264 additions and 65 deletions

View File

@ -584,27 +584,29 @@ Example of a minimal profile:
Cloning from a Snapshot
=======================
.. versionadded:: 2016.3.4
Cloning a template works similar to cloning a VM except for the fact that
a snapshot number must be provided.
.. versionadded:: 2016.3.5
Cloning from a snapshot requires that one of the
supported options be set in the cloud profile.
Supported options are ``createNewChildDiskBacking``,
``moveChildMostDiskBacking``, ``moveAllDiskBackingsAndAllowSharing``
and ``moveAllDiskBackingsAndDisallowSharing``.
Example of a minimal profile:
.. code-block:: yaml
my-template-clone:
provider: vcenter01
clonefrom: 'salt_vm'
snapshot: 3
.. image:: /_static/snapshot_manager.png
:align: center
:scale: 70%
.. note::
The previous diagram shows how to identify the snapshot number. Selected
(third snapshot) is number 3.
provider: vcenter01
clonefrom: 'salt_vm'
snapshot:
disk_move_type: createNewChildDiskBacking
# these types are also supported
# disk_move_type: moveChildMostDiskBacking
# disk_move_type: moveAllDiskBackingsAndAllowSharing
# disk_move_type: moveAllDiskBackingsAndDisallowSharing
Creating a VM
@ -685,7 +687,7 @@ Example of a complete profile:
Specifying disk backing mode
============================
.. versionadded:: Nitrogen
.. versionadded:: 2016.3.5
Disk backing mode can now be specified when cloning a VM. This option
can be set in the cloud profile as shown in example below:
@ -703,6 +705,7 @@ can be set in the cloud profile as shown in example below:
disk:
Hard disk 1:
mode: 'independent_nonpersistent'
size: 42
Hard disk 2:
size: 15

View File

@ -132,11 +132,15 @@ from salt.exceptions import SaltCloudSystemExit
# Import salt cloud libs
import salt.config as config
from salt.ext.six.moves import range
# Attempt to import pyVim and pyVmomi libs
ESX_5_5_NAME_PORTION = 'VMware ESXi 5.5'
SAFE_ESX_5_5_CONTROLLER_KEY_INDEX = 200
FLATTEN_DISK_FULL_CLONE = 'moveAllDiskBackingsAndDisallowSharing'
COPY_ALL_DISKS_FULL_CLONE = 'moveAllDiskBackingsAndAllowSharing'
CURRENT_STATE_LINKED_CLONE = 'moveChildMostDiskBacking'
QUICK_LINKED_CLONE = 'createNewChildDiskBacking'
try:
from pyVmomi import vim
HAS_PYVMOMI = True
@ -624,28 +628,38 @@ def _manage_devices(devices, vm=None, container_ref=None):
# there is at least one disk specified to be created/configured
unit_number += 1
existing_disks_label.append(device.deviceInfo.label)
# log.info('all = %s', str(devices['disk'].keys()))
if device.deviceInfo.label in list(devices['disk'].keys()):
disk_spec = None
if 'size' in devices['disk'][device.deviceInfo.label]:
disk_spec = _get_size_spec(device, devices)
size_gb = float(devices['disk'][device.deviceInfo.label]['size'])
else:
raise SaltCloudSystemExit(
'Please specify a size'
' for the disk "{0}" in'
' your cloud profile'
''.format(device.deviceInfo.label))
size_kb = int(size_gb * 1024 * 1024)
if device.capacityInKB < size_kb:
# expand the disk
disk_spec = _edit_existing_hard_disk_helper(device, size_kb)
if 'mode' in devices['disk'][device.deviceInfo.label]:
if devices['disk'][device.deviceInfo.label]['mode'] \
in [
'independent_persistent',
'persistent',
'independent_nonpersistent',
'nonpersistent',
'undoable',
'append'
'dependent',
]:
disk_spec = _get_mode_spec(device, devices, disk_spec)
else:
raise SaltCloudSystemExit('Invalid disk'
' backing mode'
' specified!')
if disk_spec:
device_specs.append(disk_spec)
device_specs.append(disk_spec)
elif isinstance(device.backing, vim.vm.device.VirtualEthernetCard.NetworkBackingInfo) or isinstance(device.backing, vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo):
# this is a network adapter
@ -2339,18 +2353,6 @@ def create(vm_):
raise SaltCloudSystemExit(
'The VM/template that you have specified under clonefrom does not exist.'
)
snapshot = None
if clone_type == 'vm' and 'snapshot' in vm_:
num = int(vm_['snapshot']) - 1
snapshot = object_ref.rootSnapshot[0]
# Drill down to the correct snapshot number
for _ in range(num):
try:
snapshot = snapshot.childSnapshot[0]
except IndexError:
raise SaltCloudSystemExit('Specified snapshot'
' does not exist.')
else:
clone_type = None
object_ref = None
@ -2498,20 +2500,18 @@ def create(vm_):
config_spec.extraConfig.append(option)
if 'clonefrom' in vm_:
if not snapshot:
# Create the clone specs
clone_spec = vim.vm.CloneSpec(
template=template,
location=reloc_spec,
config=config_spec
)
else:
clone_spec = vim.vm.CloneSpec(
template=template,
location=reloc_spec,
config=config_spec,
snapshot=snapshot
)
clone_spec = handle_snapshot(
config_spec,
object_ref,
reloc_spec,
template,
vm_
)
if not clone_spec:
clone_spec = build_clonespec(config_spec,
object_ref,
reloc_spec,
template)
if customization and (devices and 'network' in list(devices.keys())):
global_ip = vim.vm.customization.GlobalIPSettings()
@ -2661,6 +2661,75 @@ def create(vm_):
return data
def handle_snapshot(config_spec, object_ref, reloc_spec, template, vm_):
'''
Returns a clone spec for cloning from shapshots
:rtype vim.vm.CloneSpec
'''
if 'snapshot' not in vm_:
return None
allowed_types = [
FLATTEN_DISK_FULL_CLONE,
COPY_ALL_DISKS_FULL_CLONE,
CURRENT_STATE_LINKED_CLONE,
QUICK_LINKED_CLONE,
]
clone_spec = get_clonespec_for_valid_snapshot(
config_spec,
object_ref,
reloc_spec,
template,
vm_)
if not clone_spec:
raise SaltCloudSystemExit('Invalid disk move type specified'
' supported types are'
' {0}'.format(' '.join(allowed_types)))
return clone_spec
def get_clonespec_for_valid_snapshot(config_spec, object_ref, reloc_spec, template, vm_):
'''
return clonespec only if values are valid
'''
moving = True
if QUICK_LINKED_CLONE == vm_['snapshot']['disk_move_type']:
reloc_spec.diskMoveType = QUICK_LINKED_CLONE
elif CURRENT_STATE_LINKED_CLONE == vm_['snapshot']['disk_move_type']:
reloc_spec.diskMoveType = CURRENT_STATE_LINKED_CLONE
elif COPY_ALL_DISKS_FULL_CLONE == vm_['snapshot']['disk_move_type']:
reloc_spec.diskMoveType = COPY_ALL_DISKS_FULL_CLONE
elif FLATTEN_DISK_FULL_CLONE == vm_['snapshot']['disk_move_type']:
reloc_spec.diskMoveType = FLATTEN_DISK_FULL_CLONE
else:
moving = False
if moving:
return build_clonespec(config_spec, object_ref, reloc_spec, template)
else:
return None
def build_clonespec(config_spec, object_ref, reloc_spec, template):
'''
Returns the clone spec
'''
if reloc_spec.diskMoveType == QUICK_LINKED_CLONE:
return vim.vm.CloneSpec(
template=template,
location=reloc_spec,
config=config_spec,
snapshot=object_ref.snapshot.currentSnapshot
)
else:
return vim.vm.CloneSpec(
template=template,
location=reloc_spec,
config=config_spec
)
def create_datacenter(kwargs=None, call=None):
'''
Create a new data center in this VMware environment

View File

@ -2,14 +2,15 @@
'''
Module for managing dnsmasq
'''
# Import Python libs
from __future__ import absolute_import
import logging
import os
# Import salt libs
import salt.utils
# Import python libs
import os
import logging
from salt.exceptions import CommandExecutionError
log = logging.getLogger(__name__)
@ -98,20 +99,28 @@ def set_config(config_file='/etc/dnsmasq.conf', follow=True, **kwargs):
if filename.endswith('#') and filename.endswith('#'):
continue
includes.append('{0}/{1}'.format(dnsopts['conf-dir'], filename))
ret_kwargs = {}
for key in kwargs:
# Filter out __pub keys as they should not be added to the config file
# See Issue #34263 for more information
if key.startswith('__'):
continue
ret_kwargs[key] = kwargs[key]
if key in dnsopts:
if isinstance(dnsopts[key], str):
for config in includes:
__salt__['file.sed'](path=config,
before='^{0}=.*'.format(key),
after='{0}={1}'.format(key, kwargs[key]))
before='^{0}=.*'.format(key),
after='{0}={1}'.format(key, kwargs[key]))
else:
__salt__['file.append'](config_file,
'{0}={1}'.format(key, kwargs[key]))
'{0}={1}'.format(key, kwargs[key]))
else:
__salt__['file.append'](config_file,
'{0}={1}'.format(key, kwargs[key]))
return kwargs
return ret_kwargs
def get_config(config_file='/etc/dnsmasq.conf'):
@ -148,6 +157,12 @@ def _parse_dnamasq(filename):
Generic function for parsing dnsmasq files including includes.
'''
fileopts = {}
if not os.path.isfile(filename):
raise CommandExecutionError(
'Error: No such file \'{0}\''.format(filename)
)
with salt.utils.fopen(filename, 'r') as fp_:
for line in fp_:
if not line.strip():

View File

@ -665,7 +665,10 @@ def chgroups(name, groups, append=True):
continue
group = _cmd_quote(group).lstrip('\'').rstrip('\'')
cmd = 'net localgroup "{0}" {1} /add'.format(group, name)
__salt__['cmd.run_all'](cmd, python_shell=True)
out = __salt__['cmd.run_all'](cmd, python_shell=True)
if out['retcode'] != 0:
log.error(out['stdout'])
return False
agrps = set(list_groups(name))
return len(ugrps - agrps) == 0

View File

@ -1141,6 +1141,10 @@ def upgrade(refresh=True,
'''
cmd_update = (['dist-upgrade'] if dist_upgrade else ['update']) + ['--auto-agree-with-licenses']
if skip_verify:
# The '--no-gpg-checks' needs to be placed before the Zypper command.
cmd_update.insert(0, '--no-gpg-checks')
if refresh:
refresh_db()
@ -1148,11 +1152,6 @@ def upgrade(refresh=True,
cmd_update.append('--dry-run')
if dist_upgrade:
if dryrun:
# Creates a solver test case for debugging.
log.info('Executing debugsolver and performing a dry-run dist-upgrade')
__zypper__.noraise.call(*cmd_update + ['--debug-solver'])
if fromrepo:
for repo in fromrepo:
cmd_update.extend(['--from', repo])
@ -1166,8 +1165,10 @@ def upgrade(refresh=True,
else:
log.warn('Disabling vendor changes is not supported on this Zypper version')
if skip_verify:
cmd_update.append('--no-gpg-checks')
if dryrun:
# Creates a solver test case for debugging.
log.info('Executing debugsolver and performing a dry-run dist-upgrade')
__zypper__(systemd_scope=_systemd_scope()).noraise.call(*cmd_update + ['--debug-solver'])
old = list_pkgs()

View File

@ -98,7 +98,7 @@ def query(key, keyid, method='GET', params=None, headers=None,
headers['x-amz-server-side-encryption-aws-kms-key-id'] = kms_keyid
if not location:
location = __utils__['aws.get_location']()
location = salt.utils.aws.get_location()
data = ''
payload_hash = None

View File

@ -1199,6 +1199,83 @@ class VMwareTestCase(ExtendedTestCase):
call='function')
class CloneFromSnapshotTest(TestCase):
'''
Test functionality to clone from snapshot
'''
@skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.")
def test_quick_linked_clone(self):
'''
Test that disk move type is
set to createNewChildDiskBacking
'''
self._test_clone_type(vmware.QUICK_LINKED_CLONE)
@skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.")
def test_current_state_linked_clone(self):
'''
Test that disk move type is
set to moveChildMostDiskBacking
'''
self._test_clone_type(vmware.CURRENT_STATE_LINKED_CLONE)
@skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.")
def test_copy_all_disks_full_clone(self):
'''
Test that disk move type is
set to moveAllDiskBackingsAndAllowSharing
'''
self._test_clone_type(vmware.COPY_ALL_DISKS_FULL_CLONE)
@skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.")
def test_flatten_all_all_disks_full_clone(self):
'''
Test that disk move type is
set to moveAllDiskBackingsAndDisallowSharing
'''
self._test_clone_type(vmware.FLATTEN_DISK_FULL_CLONE)
@skipIf(HAS_LIBS is False, "Install pyVmomi to be able to run this unit test.")
def test_raises_error_for_invalid_disk_move_type(self):
'''
Test that invalid disk move type
raises error
'''
with self.assertRaises(SaltCloudSystemExit):
self._test_clone_type('foobar')
def _test_clone_type(self, clone_type):
'''
Assertions for checking that a certain clone type
works
'''
obj_ref = MagicMock()
obj_ref.snapshot = vim.vm.Snapshot(None, None)
obj_ref.snapshot.currentSnapshot = vim.vm.Snapshot(None, None)
clone_spec = vmware.handle_snapshot(
vim.vm.ConfigSpec(),
obj_ref,
vim.vm.RelocateSpec(),
False,
{'snapshot': {
'disk_move_type': clone_type}})
self.assertEqual(clone_spec.location.diskMoveType, clone_type)
obj_ref2 = MagicMock()
obj_ref2.snapshot = vim.vm.Snapshot(None, None)
obj_ref2.snapshot.currentSnapshot = vim.vm.Snapshot(None, None)
clone_spec2 = vmware.handle_snapshot(
vim.vm.ConfigSpec(),
obj_ref2,
vim.vm.RelocateSpec(),
True,
{'snapshot': {
'disk_move_type': clone_type}})
self.assertEqual(clone_spec2.location.diskMoveType, clone_type)
if __name__ == '__main__':
from integration import run_tests
run_tests(VMwareTestCase, needs_daemon=False)
run_tests(CloneFromSnapshotTest, needs_daemon=False)

View File

@ -16,6 +16,7 @@ from salttesting.mock import (
)
# Import Salt Libs
from salt.exceptions import CommandExecutionError
from salt.modules import dnsmasq
# Import python libs
@ -58,6 +59,23 @@ class DnsmasqTestCase(TestCase):
with patch.object(os, 'listdir', mock):
self.assertDictEqual(dnsmasq.set_config(), {})
@patch('salt.modules.dnsmasq.get_config', MagicMock(return_value={'conf-dir': 'A'}))
def test_set_config_filter_pub_kwargs(self):
'''
Test that the kwargs returned from running the set_config function
do not contain the __pub that may have been passed through in **kwargs.
'''
mock_domain = 'local'
mock_address = '/some-test-address.local/8.8.4.4'
with patch.dict(dnsmasq.__salt__, {'file.append': MagicMock()}):
ret = dnsmasq.set_config(follow=False,
domain=mock_domain,
address=mock_address,
__pub_pid=8184,
__pub_jid=20161101194639387946,
__pub_tgt='salt-call')
self.assertEqual(ret, {'domain': mock_domain, 'address': mock_address})
def test_get_config(self):
'''
test to dumps all options from the config file.
@ -68,6 +86,14 @@ class DnsmasqTestCase(TestCase):
with patch.object(os, 'listdir', mock):
self.assertDictEqual(dnsmasq.get_config(), {'conf-dir': 'A'})
def test_parse_dnsmasq_no_file(self):
'''
Tests that a CommandExecutionError is when a filename that doesn't exist is
passed in.
'''
self.assertRaises(CommandExecutionError, dnsmasq._parse_dnamasq, 'filename')
@patch('os.path.isfile', MagicMock(return_value=True))
def test_parse_dnamasq(self):
'''
test for generic function for parsing dnsmasq files including includes.

View File

@ -369,6 +369,11 @@ class ZypperTestCase(TestCase):
zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run')
zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run', '--debug-solver')
with patch('salt.modules.zypper.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.1"}])):
ret = zypper.upgrade(dist_upgrade=True, dryrun=True, fromrepo=["Dummy", "Dummy2"], novendorchange=True)
zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run', '--from', "Dummy", '--from', 'Dummy2', '--no-allow-vendor-change')
zypper_mock.assert_any_call('dist-upgrade', '--auto-agree-with-licenses', '--dry-run', '--from', "Dummy", '--from', 'Dummy2', '--no-allow-vendor-change', '--debug-solver')
with patch('salt.modules.zypper.list_pkgs', MagicMock(side_effect=[{"vim": "1.1"}, {"vim": "1.2"}])):
ret = zypper.upgrade(dist_upgrade=True, fromrepo=["Dummy", "Dummy2"], novendorchange=True)
self.assertDictEqual(ret, {"vim": {"old": "1.1", "new": "1.2"}})