mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
Change map and profile extends merge strategy to use recursive update
Previously only top-level data elements could be overridden in a map or extended profile entry. This change adjusts map and profile evaluation to use a recursive merge using salt.utils.dictupdate.update(). This is a break with compatibility, but in the common case a cloud profile or map is not structured to truncate configuration but to add further detail to the profile that it extends. Update map docs with an example from VMware where we chan now change a network parameter without having to repeat other device information such as disk controllers.
This commit is contained in:
parent
f58d3bf4a1
commit
3ec910cf34
@ -92,25 +92,27 @@ Any top level data element from your profile may be overridden in the map file:
|
||||
- web2:
|
||||
size: t2.nano
|
||||
|
||||
Nested elements cannot be specified individually. Instead, the complete
|
||||
definition for any top level data element must be repeated. In this example a
|
||||
separate subnet is assigned to each ec2 instance:
|
||||
As of Salt Nitrogen, nested elements are merged, and can can be specified
|
||||
individually without having to repeat the complete definition for each top
|
||||
level data element. In this example a separate MAC is assigned to each VMware
|
||||
instance while inheriting device parameters for for disk and network
|
||||
configuration:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
fedora_small_aws:
|
||||
- web1:
|
||||
network_interfaces:
|
||||
- DeviceIndex: 0
|
||||
SubnetId: subnet-3bf94a72
|
||||
SecurityGroupId:
|
||||
- sg-9f644fe5
|
||||
- web2:
|
||||
network_interfaces:
|
||||
- DeviceIndex: 0
|
||||
SubnetId: subnet-c3ad9fa4
|
||||
SecurityGroupId:
|
||||
- sg-9f644fe5
|
||||
nyc-vm:
|
||||
- db1:
|
||||
devices:
|
||||
network:
|
||||
Network Adapter 1:
|
||||
mac: '44:44:44:44:44:41'
|
||||
- db2:
|
||||
devices:
|
||||
network:
|
||||
Network Adapter 1:
|
||||
mac: '44:44:44:44:44:42'
|
||||
|
||||
|
||||
|
||||
A map file may also be used with the various query options:
|
||||
|
||||
|
@ -31,6 +31,7 @@ import salt.client
|
||||
import salt.loader
|
||||
import salt.utils
|
||||
import salt.utils.cloud
|
||||
import salt.utils.dictupdate
|
||||
import salt.utils.files
|
||||
import salt.syspaths
|
||||
from salt.utils import reinit_crypto
|
||||
@ -1957,7 +1958,7 @@ class Map(Cloud):
|
||||
if len(overrides['minion']) == 0:
|
||||
del overrides['minion']
|
||||
|
||||
nodedata.update(overrides)
|
||||
nodedata = salt.utils.dictupdate.update(nodedata, overrides)
|
||||
# Add the computed information to the return data
|
||||
ret['create'][nodename] = nodedata
|
||||
# Add the node name to the defined set
|
||||
|
@ -2515,9 +2515,10 @@ def apply_vm_profiles_config(providers, overrides, defaults=None):
|
||||
vms.pop(profile)
|
||||
continue
|
||||
|
||||
extended = vms.get(extends).copy()
|
||||
extended = deepcopy(vms.get(extends))
|
||||
extended.pop('profile')
|
||||
extended.update(details)
|
||||
# Merge extended configuration with base profile
|
||||
extended = salt.utils.dictupdate.update(extended, details)
|
||||
|
||||
if ':' not in extended['provider']:
|
||||
if extended['provider'] not in providers:
|
||||
@ -2757,7 +2758,7 @@ def apply_cloud_providers_config(overrides, defaults=None):
|
||||
# Grab a copy of what should be extended
|
||||
extended = providers.get(ext_alias).get(ext_driver).copy()
|
||||
# Merge the data to extend with the details
|
||||
extended.update(details)
|
||||
extended = salt.utils.dictupdate.update(extended, details)
|
||||
# Update the providers dictionary with the merged data
|
||||
providers[alias][driver] = extended
|
||||
# Update name of the driver, now that it's populated with extended information
|
||||
|
@ -552,6 +552,33 @@ class ConfigTestCase(TestCase, integration.AdaptedConfigurationTestCaseMixIn):
|
||||
overrides,
|
||||
defaults=DEFAULT), ret)
|
||||
|
||||
def test_apply_vm_profiles_config_extend_override_success(self):
|
||||
'''
|
||||
Tests profile extends and recursively merges data elements
|
||||
'''
|
||||
self.maxDiff = None
|
||||
providers = {'test-config': {'ec2': {'profiles': {}, 'driver': 'ec2'}}}
|
||||
overrides = {'Fedora': {'image': 'test-image-2',
|
||||
'extends': 'dev-instances',
|
||||
'minion': {'grains': {'stage': 'experimental'}}},
|
||||
'conf_file': PATH,
|
||||
'dev-instances': {'ssh_username': 'test_user',
|
||||
'provider': 'test-config',
|
||||
'minion': {'grains': {'role': 'webserver'}}}}
|
||||
ret = {'Fedora': {'profile': 'Fedora',
|
||||
'ssh_username': 'test_user',
|
||||
'image': 'test-image-2',
|
||||
'minion': {'grains': {'role': 'webserver',
|
||||
'stage': 'experimental'}},
|
||||
'provider': 'test-config:ec2'},
|
||||
'dev-instances': {'profile': 'dev-instances',
|
||||
'ssh_username': 'test_user',
|
||||
'minion': {'grains': {'role': 'webserver'}},
|
||||
'provider': 'test-config:ec2'}}
|
||||
self.assertEqual(sconfig.apply_vm_profiles_config(providers,
|
||||
overrides,
|
||||
defaults=DEFAULT), ret)
|
||||
|
||||
# apply_cloud_providers_config tests
|
||||
|
||||
def test_apply_cloud_providers_config_same_providers(self):
|
||||
|
122
tests/unit/map_conf_test.py
Normal file
122
tests/unit/map_conf_test.py
Normal file
@ -0,0 +1,122 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Eric Radman <ericshane@eradman.com>`
|
||||
'''
|
||||
|
||||
# Import Python libs
|
||||
from __future__ import absolute_import
|
||||
|
||||
# Import Salt Testing libs
|
||||
from salttesting import skipIf, TestCase
|
||||
from salttesting.helpers import ensure_in_syspath
|
||||
from salttesting.mock import (
|
||||
MagicMock,
|
||||
patch,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON,
|
||||
)
|
||||
|
||||
ensure_in_syspath('../')
|
||||
|
||||
# Import Salt libs
|
||||
import salt.cloud
|
||||
|
||||
EXAMPLE_PROVIDERS = {
|
||||
'nyc_vcenter': {'vmware': {'driver': 'vmware',
|
||||
'password': '123456',
|
||||
'profiles': {'nyc-vm': {'cluster': 'nycvirt',
|
||||
'datastore': 'datastore1',
|
||||
'devices': {'disk': {'Hard disk 1': {'controller': 'SCSI controller 1',
|
||||
'size': 20}},
|
||||
'network': {'Network Adapter 1': {'mac': '44:44:44:44:44:42',
|
||||
'name': 'vlan50',
|
||||
'switch_type': 'standard'}},
|
||||
'scsi': {'SCSI controller 1': {'type': 'paravirtual'}}},
|
||||
'extra_config': {'mem.hotadd': 'yes'},
|
||||
'folder': 'coreinfra',
|
||||
'image': 'rhel6_64Guest',
|
||||
'memory': '8GB',
|
||||
'num_cpus': 2,
|
||||
'power_on': True,
|
||||
'profile': 'nyc-vm',
|
||||
'provider': 'nyc_vcenter:vmware',
|
||||
'resourcepool': 'Resources'}},
|
||||
'url': 'vca1.saltstack.com',
|
||||
'user': 'root'}}
|
||||
}
|
||||
|
||||
EXAMPLE_PROFILES = {
|
||||
'nyc-vm': {'cluster': 'nycvirt',
|
||||
'datastore': 'datastore1',
|
||||
'devices': {'disk': {'Hard disk 1': {'controller': 'SCSI controller 1',
|
||||
'size': 20}},
|
||||
'network': {'Network Adapter 1': {'mac': '44:44:44:44:44:42',
|
||||
'name': 'vlan50',
|
||||
'switch_type': 'standard'}},
|
||||
'scsi': {'SCSI controller 1': {'type': 'paravirtual'}}},
|
||||
'extra_config': {'mem.hotadd': 'yes'},
|
||||
'folder': 'coreinfra',
|
||||
'image': 'rhel6_64Guest',
|
||||
'memory': '8GB',
|
||||
'num_cpus': 2,
|
||||
'power_on': True,
|
||||
'profile': 'nyc-vm',
|
||||
'provider': 'nyc_vcenter:vmware',
|
||||
'resourcepool': 'Resources'}
|
||||
}
|
||||
|
||||
EXAMPLE_MAP = {
|
||||
'nyc-vm': {'db1': {'cpus': 4,
|
||||
'devices': {'disk': {'Hard disk 1': {'size': 40}},
|
||||
'network': {'Network Adapter 1': {'mac': '22:4a:b2:92:b3:eb'}}},
|
||||
'memory': '16GB',
|
||||
'name': 'db1'}}
|
||||
}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class MapConfTest(TestCase):
|
||||
'''
|
||||
Validate evaluation of salt-cloud map configuration
|
||||
'''
|
||||
|
||||
@patch('salt.cloud.Map.read', MagicMock(return_value=EXAMPLE_MAP))
|
||||
def test_cloud_map_merge_conf(self):
|
||||
self.maxDiff = None
|
||||
'''
|
||||
Ensure that nested values can be selectivly overridden in a map file
|
||||
'''
|
||||
opts = {'extension_modules': '/var/cache/salt/master/extmods',
|
||||
'providers': EXAMPLE_PROVIDERS, 'profiles': EXAMPLE_PROFILES}
|
||||
cloud_map = salt.cloud.Map(opts)
|
||||
merged_profile = {
|
||||
'create': {'db1': {'cluster': 'nycvirt',
|
||||
'cpus': 4,
|
||||
'datastore': 'datastore1',
|
||||
'devices': {'disk': {'Hard disk 1': {'controller': 'SCSI controller 1',
|
||||
'size': 40}},
|
||||
'network': {'Network Adapter 1': {'mac': '22:4a:b2:92:b3:eb',
|
||||
'name': 'vlan50',
|
||||
'switch_type': 'standard'}},
|
||||
'scsi': {'SCSI controller 1': {'type': 'paravirtual'}}},
|
||||
'driver': 'vmware',
|
||||
'extra_config': {'mem.hotadd': 'yes'},
|
||||
'folder': 'coreinfra',
|
||||
'image': 'rhel6_64Guest',
|
||||
'memory': '16GB',
|
||||
'name': 'db1',
|
||||
'num_cpus': 2,
|
||||
'password': '123456',
|
||||
'power_on': True,
|
||||
'profile': 'nyc-vm',
|
||||
'provider': 'nyc_vcenter:vmware',
|
||||
'resourcepool': 'Resources',
|
||||
'url': 'vca1.saltstack.com',
|
||||
'user': 'root'}}
|
||||
}
|
||||
self.assertEqual(cloud_map.map_data(), merged_profile)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from integration import run_tests
|
||||
run_tests(MapConfTest, needs_daemon=False)
|
Loading…
Reference in New Issue
Block a user