summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/_ext/vyos.py159
-rw-r--r--docs/contributing/documentation.rst38
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
""""""