<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def default_campaign( campaign=None, expandcampvars=True, exclude_nodes=None, frozen=True ):
 """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 """

    campaign = campaign or nameddict()

    def _merger(_camp, _deft):
        for key in _deft.keys():
            if (
                key in _camp
                and isinstance(_camp[key], dict)
                and isinstance(_deft[key], collections.Mapping)
            ):
                _merger(_camp[key], _deft[key])
            elif key not in _camp:
                _camp[key] = _deft[key]

    _merger(campaign, DEFAULT_CAMPAIGN)
    campaign.setdefault('campaign_id', str(uuid.uuid4()))

    for precondition in campaign.precondition.keys():
        config = campaign.precondition[precondition]
        if not isinstance(config, list):
            campaign.precondition[precondition] = [config]

    def _expandvars(value):
        if isinstance(value, six.string_types):
            return expandvars(value)
        return value

    if expandcampvars:
        campaign = nameddict(dict_map_kv(campaign, _expandvars))
    else:
        campaign = nameddict(campaign)
    if expandcampvars:
        if campaign.network.get('tags') is None:
            campaign.network['tags'] = {}
        NetworkConfig(campaign).expand()
    return freeze(campaign) if frozen else campaign