def _state_overridden(self, 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 """ # overridden is the same as replaced behavior except for the scope. cmds = [] existing_interfaces = [] for h in have: existing_interfaces.append(h['name']) obj_in_want = search_obj_in_list(h['name'], want, 'name') if obj_in_want: if h != obj_in_want: replaced_cmds = self._state_replaced(obj_in_want, [h]) if replaced_cmds: cmds.extend(replaced_cmds) else: cmds.extend(self.del_attribs(h)) for w in want: if w['name'] not in existing_interfaces: # This is an object that was excluded from the 'have' list # because all of its params are currently set to default states # -OR- it's a new object that does not exist on the device yet. cmds.extend(self.add_commands(w)) return cmds
def _state_overridden(self, want, have): """ The command generator when state is overridden Scope includes all interface objects on the device. :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ # overridden behavior is the same as replaced except for scope. cmds = [] existing_vlans = [] for i in have: obj_in_want = search_obj_in_list(i['name'], want, 'name') if obj_in_want: if i != obj_in_want: v4_cmds = self._v4_cmds(obj_in_want.pop('ipv4', []), i.pop('ipv4', []), state='overridden') replaced_cmds = self._state_replaced(obj_in_want, [i]) replaced_cmds.extend(v4_cmds) self.cmd_order_fixup(replaced_cmds, obj_in_want['name']) cmds.extend(replaced_cmds) else: deleted_cmds = self.generate_delete_commands(i) self.cmd_order_fixup(deleted_cmds, i['name']) cmds.extend(deleted_cmds) for i in want: if [item for item in have if i['name'] == item['name']]: continue cmds.extend(self.add_commands(i, name=i['name'])) return cmds
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
def _state_overridden(self, want, have): """ The command generator when state is overridden. Scope includes all vlan objects on the device. :rtype: A list :returns: the minimum command set required to migrate the current configuration to the desired configuration. """ # overridden behavior is the same as replaced except for scope. cmds = [] existing_vlans = [] for h in have: existing_vlans.append(h['vlan_id']) obj_in_want = search_obj_in_list(h['vlan_id'], want, 'vlan_id') if obj_in_want: if h != obj_in_want: replaced_cmds = self._state_replaced(obj_in_want, [h]) if replaced_cmds: cmds.extend(replaced_cmds) else: cmds.append('no vlan %s' % h['vlan_id']) # Add wanted vlans that don't exist on the device yet for w in want: if w['vlan_id'] not in existing_vlans: new_vlan = ['vlan %s' % w['vlan_id']] cmds.extend(new_vlan + self.add_commands(w)) return cmds
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
def _state_overridden(self, 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 h in have: h = flatten_dict(h) obj_in_want = flatten_dict(search_obj_in_list(h['name'], want, 'name')) if h == obj_in_want: continue for w in want: w = flatten_dict(w) if h['name'] == w['name']: wkeys = w.keys() hkeys = h.keys() for k in wkeys: if k in self.exclude_params and k in hkeys: del h[k] commands.extend(self.del_attribs(h)) for w in want: commands.extend(self.set_commands(flatten_dict(w), have)) 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 = [] 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
def set_commands(self, w, have): commands = [] obj_in_have = search_obj_in_list(w['name'], have, 'name') if not obj_in_have: commands = self.add_commands(w) else: diff = self.diff_of_dicts(w, obj_in_have) commands = self.add_commands(diff, obj_in_have) return commands
def set_commands(self, want, have): cmds = [] obj_in_have = flatten_dict( search_obj_in_list(want['name'], have, 'name')) if not obj_in_have: cmds = self.add_commands(want) else: diff = self.diff_of_dicts(want, obj_in_have) cmds = self.add_commands(diff) return cmds
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
def _state_deleted(self, 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 = [] if want: for w in want: obj_in_have = search_obj_in_list(w['name'], have, 'name') commands.extend(self.del_all_attribs(obj_in_have)) else: if not have: return commands for h in have: commands.extend(self.del_all_attribs(h)) return commands
def _state_deleted(self, 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 = [] if want: for w in want: obj_in_have = search_obj_in_list(w['vlan_id'], have, 'vlan_id') if obj_in_have: commands.append('no vlan ' + str(obj_in_have['vlan_id'])) else: if not have: return commands for h in have: commands.append('no vlan ' + str(h['vlan_id'])) return commands
def _state_replaced(self, want, have): """ The command generator when state is replaced Scope is limited to interface objects defined in the playbook. :rtype: A list :returns: the commands necessary to migrate the current configuration to the desired configuration """ cmds = [] name = want['name'] obj_in_have = search_obj_in_list(want['name'], have, 'name') have_v4 = obj_in_have.pop('ipv4', []) if obj_in_have else [] have_v6 = obj_in_have.pop('ipv6', []) if obj_in_have else [] # Process lists of dicts separately v4_cmds = self._v4_cmds(want.pop('ipv4', []), have_v4, state='replaced') v6_cmds = self._v6_cmds(want.pop('ipv6', []), have_v6, state='replaced') # Process remaining attrs if obj_in_have: # Find 'want' changes first diff = self.diff_of_dicts(want, obj_in_have) rmv = {'name': name} haves_not_in_want = set(obj_in_have.keys()) - set( want.keys()) - set(diff.keys()) for i in haves_not_in_want: rmv[i] = obj_in_have[i] cmds.extend(self.generate_delete_commands(rmv)) else: diff = want cmds.extend(self.add_commands(diff, name=name)) cmds.extend(v4_cmds) cmds.extend(v6_cmds) self.cmd_order_fixup(cmds, name) return cmds
def set_commands(self, w, have): commands = [] name = w['name'] obj_in_have = search_obj_in_list(name, have, 'name') if not obj_in_have: commands = self.add_commands(w, name=name) else: # lists of dicts must be processed separately from non-list attrs v4_cmds = self._v4_cmds(w.pop('ipv4', []), obj_in_have.pop('ipv4', []), state='merged') v6_cmds = self._v6_cmds(w.pop('ipv6', []), obj_in_have.pop('ipv6', []), state='merged') # diff remaining attrs diff = self.diff_of_dicts(w, obj_in_have) commands = self.add_commands(diff, name=name) commands.extend(v4_cmds) commands.extend(v6_cmds) self.cmd_order_fixup(commands, name) return commands
def _state_overridden(self, 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 """ cmds = [] for h in have: # Check existing states, set to default if not in want or different than want h = flatten_dict(h) obj_in_want = search_obj_in_list(h['name'], want, 'name') if obj_in_want: # Let the 'want' loop handle all vals for this interface continue cmds.extend(self.del_attribs(h)) for w in want: # Update any want attrs if needed. The overridden state considers # the play as the source of truth for the entire device, therefore # set any unspecified attrs to their default state. w = self.set_none_vals_to_defaults(flatten_dict(w)) cmds.extend(self.set_commands(w, have)) return cmds