mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Merge branch 'carbon' into '2016.11'
No conflicts.
This commit is contained in:
commit
4132929832
@ -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
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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"}})
|
||||
|
Loading…
Reference in New Issue
Block a user