diff --git a/lib/ansible/module_utils/netcfg.py b/lib/ansible/module_utils/netcfg.py index f916956105b..085fe18a649 100644 --- a/lib/ansible/module_utils/netcfg.py +++ b/lib/ansible/module_utils/netcfg.py @@ -28,6 +28,7 @@ from ansible.module_utils.network import to_list DEFAULT_COMMENT_TOKENS = ['#', '!'] + class ConfigLine(object): def __init__(self, text): @@ -106,6 +107,19 @@ def parse(lines, indent, comment_tokens=None): return config +def dumps(objects, output='block'): + if output == 'block': + items = [c.raw for c in objects] + elif output == 'commands': + items = [c.text for c in objects] + elif output == 'lines': + items = list() + for obj in objects: + line = list() + line.extend([p.text for p in obj.parents]) + line.append(obj.text) + items.append(' '.join(line)) + return '\n'.join(items) class NetworkConfig(object): @@ -113,6 +127,10 @@ class NetworkConfig(object): self.indent = indent or 1 self._config = list() self._device_os = device_os + self._syntax = 'block' # block, lines, junos + + if self._device_os == 'junos': + self._syntax = 'junos' if contents: self.load(contents) @@ -121,21 +139,10 @@ class NetworkConfig(object): def items(self): return self._config - @property - def lines(self): - lines = list() - for item, next_item in get_next(self.items): - if next_item is None: - lines.append(item.line) - elif not next_item.line.startswith(item.line): - lines.append(item.line) - return lines - def __str__(self): if self._device_os == 'junos': - lines = self.to_lines(self.expand(self.items)) - return '\n'.join(lines) - return self.to_block(self.expand(self.items)) + return dumps(self.expand_line(self.items), 'lines') + return dumps(self.expand_line(self.items)) def load(self, contents): self._config = parse(contents, indent=self.indent) @@ -152,6 +159,21 @@ class NetworkConfig(object): if parents == path[:-1]: return item + def get_object(self, path): + for item in self.items: + if item.text == path[-1]: + parents = [p.text for p in item.parents] + if parents == path[:-1]: + return item + + def get_section_objects(self, path): + if not isinstance(path, list): + path = [path] + obj = self.get_object(path) + if not obj: + raise ValueError('path does not exist in config') + return self.expand_section(obj) + def search(self, regexp, path=None): regex = re.compile(r'^%s' % regexp, re.M) @@ -177,7 +199,7 @@ class NetworkConfig(object): regexp = r'%s' % regexp return re.findall(regexp, str(self)) - def expand(self, objs): + def expand_line(self, objs): visited = set() expanded = list() for o in objs: @@ -189,35 +211,6 @@ class NetworkConfig(object): visited.add(o) return expanded - def to_lines(self, objects): - lines = list() - for obj in objects: - line = list() - line.extend([p.text for p in obj.parents]) - line.append(obj.text) - lines.append(' '.join(line)) - return lines - - def to_block(self, section): - return '\n'.join([item.raw for item in section]) - - def get_section(self, path): - try: - section = self.get_section_objects(path) - if self._device_os == 'junos': - return self.to_lines(section) - return self.to_block(section) - except ValueError: - return list() - - def get_section_objects(self, path): - if not isinstance(path, list): - path = [path] - obj = self.get_object(path) - if not obj: - raise ValueError('path does not exist in config') - return self.expand_section(obj) - def expand_section(self, configobj, S=None): if S is None: S = list() @@ -228,74 +221,73 @@ class NetworkConfig(object): self.expand_section(child, S) return S - def get_object(self, path): - for item in self.items: - if item.text == path[-1]: - parents = [p.text for p in item.parents] - if parents == path[:-1]: - return item - - def get_children(self, path): - obj = self.get_object(path) - if obj: - return obj.children + def expand_block(self, objects, visited=None): + items = list() - def difference(self, other, path=None, match='line', replace='line'): - updates = list() + if not visited: + visited = set() - config = self.items - if path: - config = self.get_children(path) or list() + for o in objects: + items.append(o) + visited.add(o) + for child in o.children: + items.extend(self.expand_block([child], visited)) - if match == 'line': - for item in config: - if item not in other.items: - updates.append(item) + return items - elif match == 'strict': - if path: - current = other.get_children(path) or list() - else: - current = other.items + def diff_line(self, other): + diff = list() + for item in self.items: + if item not in other.items: + diff.append(item) + return diff + + def diff_strict(self, other): + diff = list() + for index, item in enumerate(self.items): + try: + if item != other.items[index]: + diff.append(item) + except IndexError: + diff.append(item) + return diff + + def diff_exact(self, other): + diff = list() + if len(other.items) != len(self.items): + diff.extend(self.items) + else: + for ours, theirs in itertools.izip(self.items, other.items): + if ours != theirs: + diff.extend(self.items) + break + return diff - for index, item in enumerate(config): - try: - if item != current[index]: - updates.append(item) - except IndexError: - updates.append(item) - - elif match == 'exact': - if path: - current = other.get_children(path) or list() - else: - current = other.items - if len(current) != len(config): - updates.extend(config) - else: - for ours, theirs in itertools.izip(config, current): - if ours != theirs: - updates.extend(config) - break + def difference(self, other, match='line', replace='line'): + try: + func = getattr(self, 'diff_%s' % match) + updates = func(other) + except AttributeError: + raise TypeError('invalid value for match keyword') if self._device_os == 'junos': return updates - changes = list() - for update in updates: - if replace == 'block': - if update.parents: - changes.append(update.parents[-1]) - for child in update.parents[-1].children: - changes.append(child) + if replace == 'block': + parents = list() + for u in updates: + if u.parents is None: + if u not in parents: + parents.append(u) else: - changes.append(update) - else: - changes.append(update) - updates = self.expand(changes) + for p in u.parents: + if p not in parents: + parents.append(p) + + return self.expand_block(parents) - return [item.text for item in updates] + return self.expand_line(updates) def replace(self, patterns, repl, parents=None, add_if_missing=False, ignore_whitespace=True):