def default_campaign(<EOL>campaign=None, expandcampvars=True, exclude_nodes=None, frozen=True<EOL>):
campaign = campaign or nameddict()<EOL>def _merger(_camp, _deft):<EOL><INDENT>for key in _deft.keys():<EOL><INDENT>if (<EOL>key in _camp<EOL>and isinstance(_camp[key], dict)<EOL>and isinstance(_deft[key], collections.Mapping)<EOL>):<EOL><INDENT>_merger(_camp[key], _deft[key])<EOL><DEDENT>elif key not in _camp:<EOL><INDENT>_camp[key] = _deft[key]<EOL><DEDENT><DEDENT><DEDENT>_merger(campaign, DEFAULT_CAMPAIGN)<EOL>campaign.setdefault('<STR_LIT>', str(uuid.uuid4()))<EOL>for precondition in campaign.precondition.keys():<EOL><INDENT>config = campaign.precondition[precondition]<EOL>if not isinstance(config, list):<EOL><INDENT>campaign.precondition[precondition] = [config]<EOL><DEDENT><DEDENT>def _expandvars(value):<EOL><INDENT>if isinstance(value, six.string_types):<EOL><INDENT>return expandvars(value)<EOL><DEDENT>return value<EOL><DEDENT>if expandcampvars:<EOL><INDENT>campaign = nameddict(dict_map_kv(campaign, _expandvars))<EOL><DEDENT>else:<EOL><INDENT>campaign = nameddict(campaign)<EOL><DEDENT>if expandcampvars:<EOL><INDENT>if campaign.network.get('<STR_LIT>') is None:<EOL><INDENT>campaign.network['<STR_LIT>'] = {}<EOL><DEDENT>NetworkConfig(campaign).expand()<EOL><DEDENT>return freeze(campaign) if frozen else campaign<EOL>
Fill an existing campaign with default
    values for optional keys

    :param campaign: dictionary
    :type campaign: str
    :param exclude_nodes: node set to exclude from allocations
    :type exclude_nodes: str
    :param expandcampvars: should env variables be expanded? True by default
    :type expandcampvars: bool
    :param frozen: whether the returned data-structure is immutable or not
    :type frozen: bool
    :return: object provided in parameter
    :rtype: dictionary