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 """""" |