def _state_overridden(want, have):
        """ The command generator when state is overridden

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        commands = []
        for vlan_id, extant in have.items():
            if vlan_id in want:
                desired = want[vlan_id]
            else:
                desired = dict()

            add_config = dict_diff(extant, desired)
            del_config = dict_diff(desired, extant)

            commands.extend(generate_commands(vlan_id, add_config, del_config))

        # Handle vlans not already in config
        new_vlans = [vlan_id for vlan_id in want if vlan_id not in have]
        for vlan_id in new_vlans:
            desired = want[vlan_id]
            extant = dict(vlan_id=vlan_id)
            add_config = dict_diff(extant, desired)

            commands.extend(generate_commands(vlan_id, add_config, {}))

        return commands
    def _render_updates(self, want, have):
        commands = []

        temp_have_members = have.pop('members', None)
        temp_want_members = want.pop('members', None)

        updates = dict_diff(have, want)

        if temp_have_members:
            have['members'] = temp_have_members
        if temp_want_members:
            want['members'] = temp_want_members

        commands.extend(self._add_bond_members(want, have))

        if updates:
            for key, value in iteritems(updates):
                if value:
                    if key == 'arp_monitor':
                        commands.extend(
                            self._add_arp_monitor(updates, key, want, have)
                        )
                    else:
                        commands.append(self._compute_command(have['name'], key, str(value)))
        return commands
示例#3
0
    def _state_merged(self, want, have):
        """ The requests generator when state is merged

        :rtype: A list
        :returns: the requests necessary to merge the provided into
                  the current configuration
        """
        requests = []

        request_patch = deepcopy(self.VLAN_PATCH)

        for w in want:
            if w.get('vlan_id'):
                h = search_obj_in_list(w['vlan_id'], have, 'vlan_id')
                if h:
                    if dict_diff(w, h):
                        request_body = self._update_patch_request(w)
                        request_patch["data"]["openconfig-vlan:vlans"][
                            "vlan"].append(request_body)
                else:
                    request_post = self._update_post_request(w)
                    requests.append(request_post)

        if len(request_patch["data"]["openconfig-vlan:vlans"]["vlan"]):
            request_patch["data"] = json.dumps(request_patch["data"])
            requests.append(request_patch)
        return requests
    def _state_replaced(self, want, have):
        """ The command generator when state is replaced

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        cmds = []
        obj_in_have = search_obj_in_list(want['name'], have, 'name')
        if obj_in_have:
            diff = dict_diff(want, obj_in_have)
        else:
            diff = want
        merged_cmds = self.set_commands(want, have)
        if 'name' not in diff:
            diff['name'] = want['name']

        replaced_cmds = []
        if obj_in_have:
            replaced_cmds = self.del_attribs(diff)
        if replaced_cmds or merged_cmds:
            for cmd in set(replaced_cmds).intersection(set(merged_cmds)):
                merged_cmds.remove(cmd)
            cmds.extend(replaced_cmds)
            cmds.extend(merged_cmds)
        return cmds
示例#5
0
    def _state_replaced(self, want, have):
        """ The command generator when state is replaced.
        Scope is limited to vlan objects defined in the playbook.
        :rtype: A list
        :returns: The minimum command set required to migrate the current
                  configuration to the desired configuration.
        """
        obj_in_have = search_obj_in_list(want['vlan_id'], have, 'vlan_id')
        if obj_in_have:
            # ignore states that are already reset, then diff what's left
            obj_in_have = self.remove_default_states(obj_in_have)
            diff = dict_diff(want, obj_in_have)
            # Remove merge items from diff; what's left will be used to
            # remove states not specified in the playbook
            for k in dict(set(want.items()) - set(obj_in_have.items())).keys():
                diff.pop(k, None)
        else:
            diff = want

        # merged_cmds: 'want' cmds to update 'have' states that don't match
        # replaced_cmds: remaining 'have' cmds that need to be reset to default
        merged_cmds = self.set_commands(want, have)
        replaced_cmds = []
        if obj_in_have:
            # Remaining diff items are used to reset states to default
            replaced_cmds = self.del_attribs(diff)
        cmds = []
        if replaced_cmds or merged_cmds:
            cmds += ['vlan %s' % str(want['vlan_id'])]
            cmds += merged_cmds + replaced_cmds
        return cmds
示例#6
0
    def _render_updates(self, want, have):
        commands = []
        if have:
            temp_have_legacy_protos = have.pop('legacy_protocols', None)
        else:
            have = {}
            temp_want_legacy_protos = want.pop('legacy_protocols', None)

        updates = dict_diff(have, want)

        if have and temp_have_legacy_protos:
            have['legacy_protocols'] = temp_have_legacy_protos
        if not have and temp_want_legacy_protos:
            want['legacy_protocols'] = temp_want_legacy_protos

        commands.extend(self._add_lldp_protocols(want, have))

        if updates:
            for key, value in iteritems(updates):
                if value:
                    if key == 'enable':
                        commands.append(self._compute_command())
                    elif key == 'address':
                        commands.append(
                            self._compute_command('management-address',
                                                  str(value)))
                    elif key == 'snmp':
                        if value == 'disable':
                            commands.append(
                                self._compute_command(key, remove=True))
                        else:
                            commands.append(
                                self._compute_command(key, str(value)))
        return commands
示例#7
0
 def get_diff(self, comparable, base):
     diff = {}
     if not base:
         diff = comparable
     else:
         diff = dict_diff(base, comparable)
     return diff
    def _state_replaced(self, w, have):
        """ The command generator when state is replaced

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        commands = []
        obj_in_have = flatten_dict(search_obj_in_list(w['name'], have, 'name'))
        if obj_in_have:
            diff = dict_diff(w, obj_in_have)
        else:
            diff = w
        merged_commands = self.set_commands(w, have)
        if 'name' not in diff:
            diff['name'] = w['name']
        wkeys = w.keys()
        dkeys = diff.keys()
        for k in wkeys:
            if k in self.exclude_params and k in dkeys:
                del diff[k]
        replaced_commands = self.del_attribs(diff)

        if merged_commands:
            cmds = set(replaced_commands).intersection(set(merged_commands))
            for cmd in cmds:
                merged_commands.remove(cmd)
            commands.extend(replaced_commands)
            commands.extend(merged_commands)
        return commands
示例#9
0
    def _state_overridden(self, want, have):
        """ The request generator when state is overridden

        :rtype: A list
        :returns: the requests necessary to migrate the current configuration
                  to the desired configuration
        """
        requests = []
        request_patch = deepcopy(self.VLAN_PATCH)

        have_copy = []
        for w in want:
            if w.get('vlan_id'):
                h = search_obj_in_list(w['vlan_id'], have, 'vlan_id')
                if h:
                    if dict_diff(w, h):
                        request_body = self._update_patch_request(w)
                        request_patch["data"]["openconfig-vlan:vlans"][
                            "vlan"].append(request_body)
                    have_copy.append(h)
                else:
                    request_post = self._update_post_request(w)
                    requests.append(request_post)

        for h in have:
            if h not in have_copy and h['vlan_id'] != 1:
                request_delete = self._update_delete_request(h)
                requests.append(request_delete)

        if len(request_patch["data"]["openconfig-vlan:vlans"]["vlan"]):
            request_patch["data"] = json.dumps(request_patch["data"])
            requests.append(request_patch)

        return requests
示例#10
0
    def _add_location(self, name, want_item, have_item):
        commands = []
        have_dict = {}
        have_ca = {}
        set_cmd = name + ' location '
        want_location_type = want_item.get('location') or {}
        have_location_type = have_item.get('location') or {}

        if want_location_type['coordinate_based']:
            want_dict = want_location_type.get('coordinate_based') or {}
            if is_dict_element_present(have_location_type, 'coordinate_based'):
                have_dict = have_location_type.get('coordinate_based') or {}
            location_type = 'coordinate-based'
            updates = dict_diff(have_dict, want_dict)
            for key, value in iteritems(updates):
                if value:
                    commands.append(
                        self._compute_command(set_cmd + location_type, key, str(value))
                    )

        elif want_location_type['civic_based']:
            location_type = 'civic-based'
            want_dict = want_location_type.get('civic_based') or {}
            want_ca = want_dict.get('ca_info') or []
            if is_dict_element_present(have_location_type, 'civic_based'):
                have_dict = have_location_type.get('civic_based') or {}
                have_ca = have_dict.get('ca_info') or []
                if want_dict['country_code'] != have_dict['country_code']:
                    commands.append(
                        self._compute_command(
                            set_cmd + location_type, 'country-code', str(want_dict['country_code'])
                        )
                    )
            else:
                commands.append(
                    self._compute_command(
                        set_cmd + location_type, 'country-code', str(want_dict['country_code'])
                    )
                )
            commands.extend(self._add_civic_address(name, want_ca, have_ca))

        elif want_location_type['elin']:
            location_type = 'elin'
            if is_dict_element_present(have_location_type, 'elin'):
                if want_location_type.get('elin') != have_location_type.get('elin'):
                    commands.append(
                        self._compute_command(
                            set_cmd + location_type, value=str(want_location_type['elin'])
                        )
                    )
            else:
                commands.append(
                    self._compute_command(
                        set_cmd + location_type, value=str(want_location_type['elin'])
                    )
                )
        return commands
    def _state_merged(self, want, have):
        """ The command generator when state is merged

        :rtype: A list
        :returns: the commands necessary to merge the provided into
                  the current configuration
        """
        commands = []
        want_copy = deepcopy(remove_empties(want))
        have_copy = deepcopy(have)

        want_vifs = want_copy.pop('vifs', [])
        have_vifs = have_copy.pop('vifs', [])

        updates = dict_diff(have_copy, want_copy)

        if updates:
            for key, value in iteritems(updates):
                commands.append(
                    self._compute_commands(key=key,
                                           value=value,
                                           interface=want_copy['name']))

        if want_vifs:
            for want_vif in want_vifs:
                have_vif = search_obj_in_list(want_vif['vlan_id'],
                                              have_vifs,
                                              key='vlan_id')
                if not have_vif:
                    have_vif = {
                        'vlan_id': want_vif['vlan_id'],
                        'enabled': True
                    }

                vif_updates = dict_diff(have_vif, want_vif)
                if vif_updates:
                    for key, value in iteritems(vif_updates):
                        commands.append(
                            self._compute_commands(key=key,
                                                   value=value,
                                                   interface=want_copy['name'],
                                                   vif=want_vif['vlan_id']))

        return commands
def test_dict_diff():
    base = dict(obj2=dict(),
                b1=True,
                b2=False,
                b3=False,
                one=1,
                two=2,
                three=3,
                obj1=dict(key1=1, key2=2),
                l1=[1, 3],
                l2=[1, 2, 3],
                l4=[4],
                nested=dict(n1=dict(n2=2)))

    other = dict(b1=True,
                 b2=False,
                 b3=True,
                 b4=True,
                 one=1,
                 three=4,
                 four=4,
                 obj1=dict(key1=2),
                 l1=[2, 1],
                 l2=[3, 2, 1],
                 l3=[1],
                 nested=dict(n1=dict(n2=2, n3=3)))

    result = dict_diff(base, other)

    # string assertions
    assert 'one' not in result
    assert 'two' not in result
    assert result['three'] == 4
    assert result['four'] == 4

    # dict assertions
    assert 'obj1' in result
    assert 'key1' in result['obj1']
    assert 'key2' not in result['obj1']

    # list assertions
    assert result['l1'] == [2, 1]
    assert 'l2' not in result
    assert result['l3'] == [1]
    assert 'l4' not in result

    # nested assertions
    assert 'obj1' in result
    assert result['obj1']['key1'] == 2
    assert 'key2' not in result['obj1']

    # bool assertions
    assert 'b1' not in result
    assert 'b2' not in result
    assert result['b3']
    assert result['b4']
示例#13
0
    def _state_overridden(want, have):
        """ The command generator when state is overridden

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        commands = []
        for key, extant in have.items():
            if key in want:
                desired = want[key]
            else:
                desired = dict()

            add_config = dict_diff(extant, desired)
            del_config = dict_diff(desired, extant)

            commands.extend(generate_commands(key, add_config, del_config))

        return commands
    def _state_replaced(want, have):
        """ The command generator when state is replaced

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        commands = []
        for vlan_id, desired in want.items():
            if vlan_id in have:
                extant = have[vlan_id]
            else:
                extant = dict()

            add_config = dict_diff(extant, desired)
            del_config = dict_diff(desired, extant)

            commands.extend(generate_commands(vlan_id, add_config, del_config))

        return commands
def set_config(want, have):
    commands = []
    to_set = dict_diff(have, want)
    for member in to_set.get("members", []):
        channel_id = want["name"][12:]
        commands.extend([
            "interface {0}".format(member["member"]),
            "channel-group {0} mode {1}".format(channel_id, member["mode"]),
        ])

    return commands
    def _state_merged(self, want, have):
        """ The command generator when state is merged

        :rtype: A list
        :returns: the commands necessary to merge the provided into
                  the current configuration
        """
        commands = []
        diff = dict_diff(have, want)
        commands.extend(self.set_commands(diff))
        return commands
示例#17
0
    def _state_replaced(want, have):
        """ The command generator when state is replaced

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        commands = []
        for key, desired in want.items():
            interface_name = normalize_interface(key)
            if interface_name in have:
                extant = have[interface_name]
            else:
                extant = dict()

            add_config = dict_diff(extant, desired)
            del_config = dict_diff(desired, extant)

            commands.extend(generate_commands(key, add_config, del_config))

        return commands
def remove_config(want, have):
    commands = []
    if not want.get("members"):
        return ["no interface {0}".format(want["name"])]

    to_remove = dict_diff(want, have)
    for member in to_remove.get("members", []):
        commands.extend([
            "interface {0}".format(member["member"]),
            "no channel-group",
        ])

    return commands
    def _state_replaced(want, have):
        """ The command generator when state is replaced

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        commands = []
        to_set = dict_diff(have, want)
        if 'system' in to_set:
            system = to_set['system']
            if 'priority' in system:
                commands.append('lacp system-priority {0}'.format(system['priority']))

        to_del = dict_diff(want, have)
        if 'system' in to_del:
            system = to_del['system']
            system_set = to_set.get('system', {})
            if 'priority' in system and 'priority' not in system_set:
                commands.append('no lacp system-priority')

        return commands
    def _state_merged(self, want, have):
        """ The command generator when state is merged

        :rtype: A list
        :returns: the commands necessary to merge the provided into
                  the current configuration
        """
        commands = []
        updates = dict_diff(have, want)
        if updates:
            for key, value in iteritems(flatten_dict(remove_empties(updates))):
                commands.append(self._compute_commands(key, value))

        return commands
    def _state_merged(want, have):
        """ The command generator when state is merged

        :rtype: A list
        :returns: the commands necessary to merge the provided into
                  the current configuration
        """
        commands = []
        to_set = dict_diff(have, want)
        if 'system' in to_set:
            system = to_set['system']
            if 'priority' in system:
                commands.append('lacp system-priority {0}'.format(system['priority']))

        return commands
    def _state_deleted(want, have):
        """ The command generator when state is deleted

        :rtype: A list
        :returns: the commands necessary to remove the current configuration
                  of the provided objects
        """
        commands = []
        to_del = dict_diff(want, have)
        if 'system' in to_del:
            system = to_del['system']
            if 'priority' in system:
                commands.append('no lacp system-priority')

        return commands
    def _state_replaced(self, w, have):
        """ The command generator when state is replaced

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        commands = []
        name = w['name']
        obj_in_have = search_obj_in_list(name, have, 'name')
        if obj_in_have:
            # If 'w' does not specify mode then intf may need to change to its
            # default mode, however default mode may depend on sysdef.
            if not w.get('mode') and re.search('Ethernet|port-channel', name):
                sysdefs = self.intf_defs['sysdefs']
                sysdef_mode = sysdefs['mode']
                if obj_in_have.get('mode') != sysdef_mode:
                    w['mode'] = sysdef_mode
            diff = dict_diff(w, obj_in_have)
        else:
            diff = w

        merged_commands = self.set_commands(w, have)
        if merged_commands:
            # merged_commands:
            #   - These commands are changes specified by the playbook.
            #   - merged_commands apply to both existing and new objects
            # replaced_commands:
            #   - These are the unspecified commands, used to reset any params
            #     that are not already set to default states
            #   - replaced_commands should only be used on 'have' objects
            #     (interfaces that already exist)
            if obj_in_have:
                if 'name' not in diff:
                    diff['name'] = name
                wkeys = w.keys()
                dkeys = diff.keys()
                for k in wkeys:
                    if k in self.exclude_params and k in dkeys:
                        del diff[k]
                replaced_commands = self.del_attribs(diff)
                cmds = set(replaced_commands).intersection(set(merged_commands))
                for cmd in cmds:
                    merged_commands.remove(cmd)
                commands.extend(replaced_commands)

            commands.extend(merged_commands)
        return commands
示例#24
0
    def _state_merged(want, have):
        """ The command generator when state is merged

        :rtype: A list
        :returns: the commands necessary to merge the provided into
                  the current configuration
        """
        commands = []

        updates = dict_diff(have, want)
        if updates:
            for key, value in iteritems(
                    flatten_dict(remove_empties(updates['system']))):
                commands.append('lacp system {0} {1}'.format(
                    key.replace('address', 'mac'), value))

        return commands
    def _state_replaced(self, want, have):
        """ The command generator when state is replaced

        :rtype: A list
        :returns: the commands necessary to migrate the current configuration
                  to the desired configuration
        """
        commands = []
        merge_dict = dict_diff(have, want)
        #  merge_dict will contain new and unique values in want
        delete_dict = self.find_delete_params(have, want)
        self._module.params['state'] = 'deleted'
        commands.extend(self._state_deleted(delete_dict))  # delete
        self._module.params['state'] = 'merged'
        commands.extend(self.set_commands(merge_dict))  # merge
        self._module.params['state'] = 'replaced'
        return commands
    def diff_list_of_dicts(self, want, have):
        if not want:
            want = []

        if not have:
            have = []

        diff = []
        for w_item in want:
            h_item = search_obj_in_list(w_item['member'], have,
                                        key='member') or {}
            delta = dict_diff(h_item, w_item)
            if delta:
                if 'member' not in delta.keys():
                    delta['member'] = w_item['member']
                diff.append(delta)

        return diff
    def _state_merged(want, have):
        """ The command generator when state is merged

        :rtype: A list
        :returns: the commands necessary to merge the provided into
                  the current configuration
        """
        commands = []
        for vlan_id, desired in want.items():
            if vlan_id in have:
                extant = have[vlan_id]
            else:
                extant = dict()

            add_config = dict_diff(extant, desired)

            commands.extend(generate_commands(vlan_id, add_config, {}))

        return commands
示例#28
0
    def _state_merged(self, want, have):
        """ The command generator when state is merged

        :rtype: A list
        :returns: the commands necessary to merge the provided into
                  the current configuration
        """
        commands = []
        if not have:
            have = {'name': want['name']}

        for key, value in iteritems(
                flatten_dict(remove_empties(dict_diff(have, want)))):
            commands.append(self._compute_commands(key, value))

        if commands:
            pad_commands(commands, want['name'])

        return commands
示例#29
0
def diff_list_of_dicts(w, h):
    """
    Returns a list containing diff between
    two list of dictionaries
    """
    if not w:
        w = []
    if not h:
        h = []

    diff = []
    for w_item in w:
        h_item = search_obj_in_list(w_item['member'], h, key='member') or {}
        d = dict_diff(h_item, w_item)
        if d:
            if 'member' not in d.keys():
                d['member'] = w_item['member']
            diff.append(d)

    return diff
示例#30
0
    def _state_deleted(want, have):
        """ The command generator when state is deleted

        :rtype: A list
        :returns: the commands necessary to remove the current configuration
                  of the provided objects
        """
        commands = []
        for key in want:
            desired = dict()
            if key in have:
                extant = have[key]
            else:
                continue

            del_config = dict_diff(desired, extant)

            commands.extend(generate_commands(key, {}, del_config))

        return commands