diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/_ext/vyos.py | 159 | ||||
| -rw-r--r-- | docs/contributing/documentation.rst | 38 | 
2 files changed, 195 insertions, 2 deletions
| diff --git a/docs/_ext/vyos.py b/docs/_ext/vyos.py index 4ee87d63..e42d4cf0 100644 --- a/docs/_ext/vyos.py +++ b/docs/_ext/vyos.py @@ -1,6 +1,10 @@ -from docutils import nodes, utils +import re +import io +import os +from docutils import io, nodes, utils, statemachine +from docutils.utils.error_reporting import SafeString, ErrorString  from docutils.parsers.rst.roles import set_classes -from docutils.parsers.rst import Directive +from docutils.parsers.rst import Directive, directives  from sphinx.util.docutils import SphinxDirective @@ -49,6 +53,7 @@ def setup(app):      app.add_directive('cfgcmd', CfgCmdDirective)      app.add_directive('opcmd', OpCmdDirective) +    app.add_directive('cmdinclude', CfgInclude)      app.connect('doctree-resolved', process_cmd_nodes) @@ -148,6 +153,156 @@ class inlinecmd(nodes.inline):          #self.literal_whitespace -= 1 +class CfgInclude(Directive): +    required_arguments = 1 +    optional_arguments = 0 +    final_argument_whitespace = True +    option_spec = { +        'var0': str, +        'var1': str, +        'var2': str, +        'var3': str, +        'var4': str, +        'var5': str, +        'var6': str, +        'var7': str, +        'var8': str, +        'var9': str +    } + +    def run(self): +        ### Copy from include directive docutils  +        """Include a file as part of the content of this reST file.""" +        if not self.state.document.settings.file_insertion_enabled: +            raise self.warning('"%s" directive disabled.' % self.name) +        source = self.state_machine.input_lines.source( +            self.lineno - self.state_machine.input_offset - 1) +        source_dir = os.path.dirname(os.path.abspath(source)) +        path = directives.path(self.arguments[0]) +        if path.startswith('<') and path.endswith('>'): +            path = os.path.join(self.standard_include_path, path[1:-1]) +        path = os.path.normpath(os.path.join(source_dir, path)) +        path = utils.relative_path(None, path) +        path = nodes.reprunicode(path) +        encoding = self.options.get( +            'encoding', self.state.document.settings.input_encoding) +        e_handler=self.state.document.settings.input_encoding_error_handler +        tab_width = self.options.get( +            'tab-width', self.state.document.settings.tab_width) +        try: +            self.state.document.settings.record_dependencies.add(path) +            include_file = io.FileInput(source_path=path, +                                        encoding=encoding, +                                        error_handler=e_handler) +        except UnicodeEncodeError: +            raise self.severe(u'Problems with "%s" directive path:\n' +                              'Cannot encode input file path "%s" ' +                              '(wrong locale?).' % +                              (self.name, SafeString(path))) +        except IOError: +            raise self.severe(u'Problems with "%s" directive path.' % +                      (self.name)) +        startline = self.options.get('start-line', None) +        endline = self.options.get('end-line', None) +        try: +            if startline or (endline is not None): +                lines = include_file.readlines() +                rawtext = ''.join(lines[startline:endline]) +            else: +                rawtext = include_file.read() +        except UnicodeError: +            raise self.severe(u'Problem with "%s" directive:\n%s' % +                              (self.name, ErrorString(error))) +        # start-after/end-before: no restrictions on newlines in match-text, +        # and no restrictions on matching inside lines vs. line boundaries +        after_text = self.options.get('start-after', None) +        if after_text: +            # skip content in rawtext before *and incl.* a matching text +            after_index = rawtext.find(after_text) +            if after_index < 0: +                raise self.severe('Problem with "start-after" option of "%s" ' +                                  'directive:\nText not found.' % self.name) +            rawtext = rawtext[after_index + len(after_text):] +        before_text = self.options.get('end-before', None) +        if before_text: +            # skip content in rawtext after *and incl.* a matching text +            before_index = rawtext.find(before_text) +            if before_index < 0: +                raise self.severe('Problem with "end-before" option of "%s" ' +                                  'directive:\nText not found.' % self.name) +            rawtext = rawtext[:before_index] + +        include_lines = statemachine.string2lines(rawtext, tab_width, +                                                  convert_whitespace=True) +        if 'literal' in self.options: +            # Convert tabs to spaces, if `tab_width` is positive. +            if tab_width >= 0: +                text = rawtext.expandtabs(tab_width) +            else: +                text = rawtext +            literal_block = nodes.literal_block(rawtext, source=path, +                                    classes=self.options.get('class', [])) +            literal_block.line = 1 +            self.add_name(literal_block) +            if 'number-lines' in self.options: +                try: +                    startline = int(self.options['number-lines'] or 1) +                except ValueError: +                    raise self.error(':number-lines: with non-integer ' +                                     'start value') +                endline = startline + len(include_lines) +                if text.endswith('\n'): +                    text = text[:-1] +                tokens = NumberLines([([], text)], startline, endline) +                for classes, value in tokens: +                    if classes: +                        literal_block += nodes.inline(value, value, +                                                      classes=classes) +                    else: +                        literal_block += nodes.Text(value, value) +            else: +                literal_block += nodes.Text(text, text) +            return [literal_block] +        if 'code' in self.options: +            self.options['source'] = path +            codeblock = CodeBlock(self.name, +                                  [self.options.pop('code')], # arguments +                                  self.options, +                                  include_lines, # content +                                  self.lineno, +                                  self.content_offset, +                                  self.block_text, +                                  self.state, +                                  self.state_machine) +            return codeblock.run() + +        new_include_lines = [] +        var_value0 = self.options.get('var0', '') +        var_value1 = self.options.get('var1', '') +        var_value2 = self.options.get('var2', '') +        var_value3 = self.options.get('var3', '') +        var_value4 = self.options.get('var4', '') +        var_value5 = self.options.get('var5', '') +        var_value6 = self.options.get('var6', '') +        var_value7 = self.options.get('var7', '') +        var_value8 = self.options.get('var8', '') +        var_value9 = self.options.get('var9', '') +        for line in include_lines: +            line = re.sub('{{\s?var0\s?}}',var_value0,line) +            line = re.sub('{{\s?var1\s?}}',var_value1,line) +            line = re.sub('{{\s?var2\s?}}',var_value2,line) +            line = re.sub('{{\s?var3\s?}}',var_value3,line) +            line = re.sub('{{\s?var4\s?}}',var_value4,line) +            line = re.sub('{{\s?var5\s?}}',var_value5,line) +            line = re.sub('{{\s?var6\s?}}',var_value6,line) +            line = re.sub('{{\s?var7\s?}}',var_value7,line) +            line = re.sub('{{\s?var8\s?}}',var_value8,line) +            line = re.sub('{{\s?var9\s?}}',var_value9,line) +            new_include_lines.append(line) +        self.state_machine.insert_input(new_include_lines, path) +        return [] + +  class CfgcmdlistDirective(Directive):      def run(self): diff --git a/docs/contributing/documentation.rst b/docs/contributing/documentation.rst index 92af881c..e8d1dba5 100644 --- a/docs/contributing/documentation.rst +++ b/docs/contributing/documentation.rst @@ -200,6 +200,44 @@ For a inline operational level command use ``:opcmd:``    :opcmd:`add system image` +cmdinclude +"""""""""" + +To minimize redundancy there is a special include directive. It include a txt +file and replace the ``{{ var0 }}`` - ``{{ var9 }}`` with the correct value + +.. code-block:: none + +   .. cmdinclude:: interface-address.txt +      :var0: ethernet +      :var1: eth1 + +the content of interface-address.txt looks like this + +.. code-block:: none + +   .. cfgcmd:: set interfaces {{ var0 }} <interface> address <address | dhcp | +      dhcpv6> + +      Configure interface `<interface>` with one or more interface +      addresses. + +      * **address** can be specified multiple times as IPv4 and/or IPv6 +      address, e.g. 192.0.2.1/24 and/or 2001:db8::1/64 +      * **dhcp** interface address is received by DHCP from a DHCP server +      on this segment. +      * **dhcpv6** interface address is received by DHCPv6 from a DHCPv6 +      server on this segment. + +      Example: + +      .. code-block:: none + +         set interfaces {{ var0 }} {{ var1 }} address 192.0.2.1/24 +         set interfaces {{ var0 }} {{ var1 }} address 192.0.2.2/24 +         set interfaces {{ var0 }} {{ var1 }} address 2001:db8::ffff/64 +         set interfaces {{ var0 }} {{ var1 }} address 2001:db8:100::ffff/64 +  vytask  """""" | 
