1"""
2Network Config
3==============
4
5Manage the configuration on a network device given a specific static config or template.
6
7:codeauthor: Mircea Ulinic <ping@mirceaulinic.net> & Jerome Fleury <jf@cloudflare.com>
8:maturity:   new
9:depends:    napalm
10:platform:   unix
11
12Dependencies
13------------
14- :mod:`NAPALM proxy minion <salt.proxy.napalm>`
15- :mod:`Network-related basic features execution module <salt.modules.napalm_network>`
16
17.. versionadded:: 2017.7.0
18"""
19
20import logging
21
22import salt.utils.napalm
23
24log = logging.getLogger(__name__)
25
26# ----------------------------------------------------------------------------------------------------------------------
27# state properties
28# ----------------------------------------------------------------------------------------------------------------------
29
30__virtualname__ = "netconfig"
31
32# ----------------------------------------------------------------------------------------------------------------------
33# global variables
34# ----------------------------------------------------------------------------------------------------------------------
35
36# ----------------------------------------------------------------------------------------------------------------------
37# property functions
38# ----------------------------------------------------------------------------------------------------------------------
39
40
41def __virtual__():
42    """
43    NAPALM library must be installed for this module to work and run in a (proxy) minion.
44    """
45    return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__)
46
47
48# ----------------------------------------------------------------------------------------------------------------------
49# helper functions -- will not be exported
50# ----------------------------------------------------------------------------------------------------------------------
51
52
53def _update_config(
54    template_name,
55    template_source=None,
56    template_hash=None,
57    template_hash_name=None,
58    template_user="root",
59    template_group="root",
60    template_mode="755",
61    template_attrs="--------------e----",
62    saltenv=None,
63    template_engine="jinja",
64    skip_verify=False,
65    defaults=None,
66    test=False,
67    commit=True,
68    debug=False,
69    replace=False,
70    **template_vars
71):
72    """
73    Call the necessary functions in order to execute the state.
74    For the moment this only calls the ``net.load_template`` function from the
75    :mod:`Network-related basic features execution module <salt.modules.napalm_network>`, but this may change in time.
76    """
77
78    return __salt__["net.load_template"](
79        template_name,
80        template_source=template_source,
81        template_hash=template_hash,
82        template_hash_name=template_hash_name,
83        template_user=template_user,
84        template_group=template_group,
85        template_mode=template_mode,
86        template_attrs=template_attrs,
87        saltenv=saltenv,
88        template_engine=template_engine,
89        skip_verify=skip_verify,
90        defaults=defaults,
91        test=test,
92        commit=commit,
93        debug=debug,
94        replace=replace,
95        **template_vars
96    )
97
98
99# ----------------------------------------------------------------------------------------------------------------------
100# callable functions
101# ----------------------------------------------------------------------------------------------------------------------
102
103
104def replace_pattern(
105    name,
106    pattern,
107    repl,
108    count=0,
109    flags=8,
110    bufsize=1,
111    append_if_not_found=False,
112    prepend_if_not_found=False,
113    not_found_content=None,
114    search_only=False,
115    show_changes=True,
116    backslash_literal=False,
117    source="running",
118    path=None,
119    test=False,
120    replace=True,
121    debug=False,
122    commit=True,
123):
124    """
125    .. versionadded:: 2019.2.0
126
127    Replace occurrences of a pattern in the configuration source. If
128    ``show_changes`` is ``True``, then a diff of what changed will be returned,
129    otherwise a ``True`` will be returned when changes are made, and ``False``
130    when no changes are made.
131    This is a pure Python implementation that wraps Python's :py:func:`~re.sub`.
132
133    pattern
134        A regular expression, to be matched using Python's
135        :py:func:`~re.search`.
136
137    repl
138        The replacement text.
139
140    count: ``0``
141        Maximum number of pattern occurrences to be replaced. If count is a
142        positive integer ``n``, only ``n`` occurrences will be replaced,
143        otherwise all occurrences will be replaced.
144
145    flags (list or int): ``8``
146        A list of flags defined in the ``re`` module documentation from the
147        Python standard library. Each list item should be a string that will
148        correlate to the human-friendly flag name. E.g., ``['IGNORECASE',
149        'MULTILINE']``. Optionally, ``flags`` may be an int, with a value
150        corresponding to the XOR (``|``) of all the desired flags. Defaults to
151        8 (which supports 'MULTILINE').
152
153    bufsize (int or str): ``1``
154        How much of the configuration to buffer into memory at once. The
155        default value ``1`` processes one line at a time. The special value
156        ``file`` may be specified which will read the entire file into memory
157        before processing.
158
159    append_if_not_found: ``False``
160        If set to ``True``, and pattern is not found, then the content will be
161        appended to the file.
162
163    prepend_if_not_found: ``False``
164        If set to ``True`` and pattern is not found, then the content will be
165        prepended to the file.
166
167    not_found_content
168        Content to use for append/prepend if not found. If None (default), uses
169        ``repl``. Useful when ``repl`` uses references to group in pattern.
170
171    search_only: ``False``
172        If set to true, this no changes will be performed on the file, and this
173        function will simply return ``True`` if the pattern was matched, and
174        ``False`` if not.
175
176    show_changes: ``True``
177        If ``True``, return a diff of changes made. Otherwise, return ``True``
178        if changes were made, and ``False`` if not.
179
180    backslash_literal: ``False``
181        Interpret backslashes as literal backslashes for the repl and not
182        escape characters.  This will help when using append/prepend so that
183        the backslashes are not interpreted for the repl on the second run of
184        the state.
185
186    source: ``running``
187        The configuration source. Choose from: ``running``, ``candidate``, or
188        ``startup``. Default: ``running``.
189
190    path
191        Save the temporary configuration to a specific path, then read from
192        there.
193
194    test: ``False``
195        Dry run? If set as ``True``, will apply the config, discard and return
196        the changes. Default: ``False`` and will commit the changes on the
197        device.
198
199    commit: ``True``
200        Commit the configuration changes? Default: ``True``.
201
202    debug: ``False``
203        Debug mode. Will insert a new key in the output dictionary, as
204        ``loaded_config`` containing the raw configuration loaded on the device.
205
206    replace: ``True``
207        Load and replace the configuration. Default: ``True``.
208
209    If an equal sign (``=``) appears in an argument to a Salt command it is
210    interpreted as a keyword argument in the format ``key=val``. That
211    processing can be bypassed in order to pass an equal sign through to the
212    remote shell command by manually specifying the kwarg:
213
214    State SLS Example:
215
216    .. code-block:: yaml
217
218        update_policy_name:
219          netconfig.replace_pattern:
220            - pattern: OLD-POLICY-NAME
221            - repl: new-policy-name
222            - debug: true
223    """
224    ret = salt.utils.napalm.default_ret(name)
225    # the user can override the flags the equivalent CLI args
226    # which have higher precedence
227    test = test or __opts__["test"]
228    debug = __salt__["config.merge"]("debug", debug)
229    commit = __salt__["config.merge"]("commit", commit)
230    replace = __salt__["config.merge"]("replace", replace)  # this might be a bit risky
231    replace_ret = __salt__["net.replace_pattern"](
232        pattern,
233        repl,
234        count=count,
235        flags=flags,
236        bufsize=bufsize,
237        append_if_not_found=append_if_not_found,
238        prepend_if_not_found=prepend_if_not_found,
239        not_found_content=not_found_content,
240        search_only=search_only,
241        show_changes=show_changes,
242        backslash_literal=backslash_literal,
243        source=source,
244        path=path,
245        test=test,
246        replace=replace,
247        debug=debug,
248        commit=commit,
249    )
250    return salt.utils.napalm.loaded_ret(ret, replace_ret, test, debug)
251
252
253def saved(
254    name,
255    source="running",
256    user=None,
257    group=None,
258    mode=None,
259    attrs=None,
260    makedirs=False,
261    dir_mode=None,
262    replace=True,
263    backup="",
264    show_changes=True,
265    create=True,
266    tmp_dir="",
267    tmp_ext="",
268    encoding=None,
269    encoding_errors="strict",
270    allow_empty=False,
271    follow_symlinks=True,
272    check_cmd=None,
273    win_owner=None,
274    win_perms=None,
275    win_deny_perms=None,
276    win_inheritance=True,
277    win_perms_reset=False,
278    **kwargs
279):
280    """
281    .. versionadded:: 2019.2.0
282
283    Save the configuration to a file on the local file system.
284
285    name
286        Absolute path to file where to save the configuration.
287        To push the files to the Master, use
288        :mod:`cp.push <salt.modules.cp.push>` Execution function.
289
290    source: ``running``
291        The configuration source. Choose from: ``running``, ``candidate``,
292        ``startup``. Default: ``running``.
293
294    user
295        The user to own the file, this defaults to the user salt is running as
296        on the minion
297
298    group
299        The group ownership set for the file, this defaults to the group salt
300        is running as on the minion. On Windows, this is ignored
301
302    mode
303        The permissions to set on this file, e.g. ``644``, ``0775``, or
304        ``4664``.
305        The default mode for new files and directories corresponds to the
306        umask of the salt process. The mode of existing files and directories
307        will only be changed if ``mode`` is specified.
308
309        .. note::
310            This option is **not** supported on Windows.
311    attrs
312        The attributes to have on this file, e.g. ``a``, ``i``. The attributes
313        can be any or a combination of the following characters:
314        ``aAcCdDeijPsStTu``.
315
316        .. note::
317            This option is **not** supported on Windows.
318
319    makedirs: ``False``
320        If set to ``True``, then the parent directories will be created to
321        facilitate the creation of the named file. If ``False``, and the parent
322        directory of the destination file doesn't exist, the state will fail.
323
324    dir_mode
325        If directories are to be created, passing this option specifies the
326        permissions for those directories. If this is not set, directories
327        will be assigned permissions by adding the execute bit to the mode of
328        the files.
329
330        The default mode for new files and directories corresponds umask of salt
331        process. For existing files and directories it's not enforced.
332
333    replace: ``True``
334        If set to ``False`` and the file already exists, the file will not be
335        modified even if changes would otherwise be made. Permissions and
336        ownership will still be enforced, however.
337
338    backup
339        Overrides the default backup mode for this specific file. See
340        :ref:`backup_mode documentation <file-state-backups>` for more details.
341
342    show_changes: ``True``
343        Output a unified diff of the old file and the new file. If ``False``
344        return a boolean if any changes were made.
345
346    create: ``True``
347        If set to ``False``, then the file will only be managed if the file
348        already exists on the system.
349
350    encoding
351        If specified, then the specified encoding will be used. Otherwise, the
352        file will be encoded using the system locale (usually UTF-8). See
353        https://docs.python.org/3/library/codecs.html#standard-encodings for
354        the list of available encodings.
355
356    encoding_errors: ``'strict'``
357        Error encoding scheme. Default is ```'strict'```.
358        See https://docs.python.org/2/library/codecs.html#codec-base-classes
359        for the list of available schemes.
360
361    allow_empty: ``True``
362        If set to ``False``, then the state will fail if the contents specified
363        by ``contents_pillar`` or ``contents_grains`` are empty.
364
365    follow_symlinks: ``True``
366        If the desired path is a symlink follow it and make changes to the
367        file to which the symlink points.
368
369    check_cmd
370        The specified command will be run with an appended argument of a
371        *temporary* file containing the new managed contents.  If the command
372        exits with a zero status the new managed contents will be written to
373        the managed destination. If the command exits with a nonzero exit
374        code, the state will fail and no changes will be made to the file.
375
376    tmp_dir
377        Directory for temp file created by ``check_cmd``. Useful for checkers
378        dependent on config file location (e.g. daemons restricted to their
379        own config directories by an apparmor profile).
380
381    tmp_ext
382        Suffix for temp file created by ``check_cmd``. Useful for checkers
383        dependent on config file extension (e.g. the init-checkconf upstart
384        config checker).
385
386    win_owner: ``None``
387        The owner of the directory. If this is not passed, user will be used. If
388        user is not passed, the account under which Salt is running will be
389        used.
390
391    win_perms: ``None``
392        A dictionary containing permissions to grant and their propagation. For
393        example: ``{'Administrators': {'perms': 'full_control'}}`` Can be a
394        single basic perm or a list of advanced perms. ``perms`` must be
395        specified. ``applies_to`` does not apply to file objects.
396
397    win_deny_perms: ``None``
398        A dictionary containing permissions to deny and their propagation. For
399        example: ``{'Administrators': {'perms': 'full_control'}}`` Can be a
400        single basic perm or a list of advanced perms. ``perms`` must be
401        specified. ``applies_to`` does not apply to file objects.
402
403    win_inheritance: ``True``
404        True to inherit permissions from the parent directory, False not to
405        inherit permission.
406
407    win_perms_reset: ``False``
408        If ``True`` the existing DACL will be cleared and replaced with the
409        settings defined in this function. If ``False``, new entries will be
410        appended to the existing DACL. Default is ``False``.
411
412    State SLS Example:
413
414    .. code-block:: yaml
415
416        /var/backups/{{ opts.id }}/{{ salt.status.time('%s') }}.cfg:
417          netconfig.saved:
418            - source: running
419            - makedirs: true
420
421    The state SLS  above would create a backup config grouping the files by the
422    Minion ID, in chronological files. For example, if the state is executed at
423    on the 3rd of August 2018, at 5:15PM, on the Minion ``core1.lon01``, the
424    configuration would saved in the file:
425    ``/var/backups/core01.lon01/1533316558.cfg``
426    """
427    ret = __salt__["net.config"](source=source)
428    if not ret["result"]:
429        return {"name": name, "changes": {}, "result": False, "comment": ret["comment"]}
430    return __states__["file.managed"](
431        name,
432        user=user,
433        group=group,
434        mode=mode,
435        attrs=attrs,
436        makedirs=makedirs,
437        dir_mode=dir_mode,
438        replace=replace,
439        backup=backup,
440        show_changes=show_changes,
441        create=create,
442        contents=ret["out"][source],
443        tmp_dir=tmp_dir,
444        tmp_ext=tmp_ext,
445        encoding=encoding,
446        encoding_errors=encoding_errors,
447        allow_empty=allow_empty,
448        follow_symlinks=follow_symlinks,
449        check_cmd=check_cmd,
450        win_owner=win_owner,
451        win_perms=win_perms,
452        win_deny_perms=win_deny_perms,
453        win_inheritance=win_inheritance,
454        win_perms_reset=win_perms_reset,
455        **kwargs
456    )
457
458
459def managed(
460    name,
461    template_name=None,
462    template_source=None,
463    template_hash=None,
464    template_hash_name=None,
465    saltenv="base",
466    template_engine="jinja",
467    skip_verify=False,
468    context=None,
469    defaults=None,
470    test=False,
471    commit=True,
472    debug=False,
473    replace=False,
474    commit_in=None,
475    commit_at=None,
476    revert_in=None,
477    revert_at=None,
478    **template_vars
479):
480    """
481    Manages the configuration on network devices.
482
483    By default this state will commit the changes on the device. If there are no changes required, it does not commit
484    and the field ``already_configured`` from the output dictionary will be set as ``True`` to notify that.
485
486    To avoid committing the configuration, set the argument ``test`` to ``True`` (or via the CLI argument ``test=True``)
487    and will discard (dry run).
488
489    To preserve the changes, set ``commit`` to ``False`` (either as CLI argument, either as state parameter).
490    However, this is recommended to be used only in exceptional cases when there are applied few consecutive states
491    and/or configuration changes. Otherwise the user might forget that the config DB is locked and the candidate config
492    buffer is not cleared/merged in the running config.
493
494    To replace the config, set ``replace`` to ``True``. This option is recommended to be used with caution!
495
496    template_name
497        Identifies path to the template source. The template can be either stored on the local machine,
498        either remotely.
499        The recommended location is under the ``file_roots`` as specified in the master config file.
500        For example, let's suppose the ``file_roots`` is configured as:
501
502        .. code-block:: yaml
503
504            file_roots:
505              base:
506                 - /etc/salt/states
507
508        Placing the template under ``/etc/salt/states/templates/example.jinja``, it can be used as
509        ``salt://templates/example.jinja``.
510        Alternatively, for local files, the user can specify the absolute path.
511        If remotely, the source can be retrieved via ``http``, ``https`` or ``ftp``.
512
513        Examples:
514
515        - ``salt://my_template.jinja``
516        - ``/absolute/path/to/my_template.jinja``
517        - ``http://example.com/template.cheetah``
518        - ``https:/example.com/template.mako``
519        - ``ftp://example.com/template.py``
520
521        .. versionchanged:: 2019.2.0
522            This argument can now support a list of templates to be rendered.
523            The resulting configuration text is loaded at once, as a single
524            configuration chunk.
525
526    template_source: None
527        Inline config template to be rendered and loaded on the device.
528
529    template_hash: None
530        Hash of the template file. Format: ``{hash_type: 'md5', 'hsum': <md5sum>}``
531
532    template_hash_name: None
533        When ``template_hash`` refers to a remote file, this specifies the filename to look for in that file.
534
535    saltenv: base
536        Specifies the template environment. This will influence the relative imports inside the templates.
537
538    template_engine: jinja
539        The following templates engines are supported:
540
541        - :mod:`cheetah<salt.renderers.cheetah>`
542        - :mod:`genshi<salt.renderers.genshi>`
543        - :mod:`jinja<salt.renderers.jinja>`
544        - :mod:`mako<salt.renderers.mako>`
545        - :mod:`py<salt.renderers.py>`
546        - :mod:`wempy<salt.renderers.wempy>`
547
548    skip_verify: False
549        If ``True``, hash verification of remote file sources (``http://``, ``https://``, ``ftp://``) will be skipped,
550        and the ``source_hash`` argument will be ignored.
551
552        .. versionchanged:: 2017.7.1
553
554    test: False
555        Dry run? If set to ``True``, will apply the config, discard and return the changes. Default: ``False``
556        (will commit the changes on the device).
557
558    commit: True
559        Commit? Default: ``True``.
560
561    debug: False
562        Debug mode. Will insert a new key under the output dictionary, as ``loaded_config`` containing the raw
563        result after the template was rendered.
564
565        .. note::
566            This argument cannot be used directly on the command line. Instead,
567            it can be passed through the ``pillar`` variable when executing
568            either of the :py:func:`state.sls <salt.modules.state.sls>` or
569            :py:func:`state.apply <salt.modules.state.apply>` (see below for an
570            example).
571
572    commit_in: ``None``
573        Commit the changes in a specific number of minutes / hours. Example of
574        accepted formats: ``5`` (commit in 5 minutes), ``2m`` (commit in 2
575        minutes), ``1h`` (commit the changes in 1 hour)`, ``5h30m`` (commit
576        the changes in 5 hours and 30 minutes).
577
578        .. note::
579            This feature works on any platforms, as it does not rely on the
580            native features of the network operating system.
581
582        .. note::
583            After the command is executed and the ``diff`` is not satisfactory,
584            or for any other reasons you have to discard the commit, you are
585            able to do so using the
586            :py:func:`net.cancel_commit <salt.modules.napalm_network.cancel_commit>`
587            execution function, using the commit ID returned by this function.
588
589        .. warning::
590            Using this feature, Salt will load the exact configuration you
591            expect, however the diff may change in time (i.e., if an user
592            applies a manual configuration change, or a different process or
593            command changes the configuration in the meanwhile).
594
595        .. versionadded:: 2019.2.0
596
597    commit_at: ``None``
598        Commit the changes at a specific time. Example of accepted formats:
599        ``1am`` (will commit the changes at the next 1AM), ``13:20`` (will
600        commit at 13:20), ``1:20am``, etc.
601
602        .. note::
603            This feature works on any platforms, as it does not rely on the
604            native features of the network operating system.
605
606        .. note::
607            After the command is executed and the ``diff`` is not satisfactory,
608            or for any other reasons you have to discard the commit, you are
609            able to do so using the
610            :py:func:`net.cancel_commit <salt.modules.napalm_network.cancel_commit>`
611            execution function, using the commit ID returned by this function.
612
613        .. warning::
614            Using this feature, Salt will load the exact configuration you
615            expect, however the diff may change in time (i.e., if an user
616            applies a manual configuration change, or a different process or
617            command changes the configuration in the meanwhile).
618
619        .. versionadded:: 2019.2.0
620
621    revert_in: ``None``
622        Commit and revert the changes in a specific number of minutes / hours.
623        Example of accepted formats: ``5`` (revert in 5 minutes), ``2m`` (revert
624        in 2 minutes), ``1h`` (revert the changes in 1 hour)`, ``5h30m`` (revert
625        the changes in 5 hours and 30 minutes).
626
627        .. note::
628            To confirm the commit, and prevent reverting the changes, you will
629            have to execute the
630            :mod:`net.confirm_commit <salt.modules.napalm_network.confirm_commit>`
631            function, using the commit ID returned by this function.
632
633        .. warning::
634            This works on any platform, regardless if they have or don't have
635            native capabilities to confirming a commit. However, please be
636            *very* cautious when using this feature: on Junos (as it is the only
637            NAPALM core platform supporting this natively) it executes a commit
638            confirmed as you would do from the command line.
639            All the other platforms don't have this capability natively,
640            therefore the revert is done via Salt. That means, your device needs
641            to be reachable at the moment when Salt will attempt to revert your
642            changes. Be cautious when pushing configuration changes that would
643            prevent you reach the device.
644
645            Similarly, if an user or a different process apply other
646            configuration changes in the meanwhile (between the moment you
647            commit and till the changes are reverted), these changes would be
648            equally reverted, as Salt cannot be aware of them.
649
650        .. versionadded:: 2019.2.0
651
652    revert_at: ``None``
653        Commit and revert the changes at a specific time. Example of accepted
654        formats: ``1am`` (will commit and revert the changes at the next 1AM),
655        ``13:20`` (will commit and revert at 13:20), ``1:20am``, etc.
656
657        .. note::
658            To confirm the commit, and prevent reverting the changes, you will
659            have to execute the
660            :mod:`net.confirm_commit <salt.modules.napalm_network.confirm_commit>`
661            function, using the commit ID returned by this function.
662
663        .. warning::
664            This works on any platform, regardless if they have or don't have
665            native capabilities to confirming a commit. However, please be
666            *very* cautious when using this feature: on Junos (as it is the only
667            NAPALM core platform supporting this natively) it executes a commit
668            confirmed as you would do from the command line.
669            All the other platforms don't have this capability natively,
670            therefore the revert is done via Salt. That means, your device needs
671            to be reachable at the moment when Salt will attempt to revert your
672            changes. Be cautious when pushing configuration changes that would
673            prevent you reach the device.
674
675            Similarly, if an user or a different process apply other
676            configuration changes in the meanwhile (between the moment you
677            commit and till the changes are reverted), these changes would be
678            equally reverted, as Salt cannot be aware of them.
679
680        .. versionadded:: 2019.2.0
681
682    replace: False
683        Load and replace the configuration. Default: ``False`` (will apply load merge).
684
685    context: None
686        Overrides default context variables passed to the template.
687
688        .. versionadded:: 2019.2.0
689
690    defaults: None
691        Default variables/context passed to the template.
692
693    template_vars
694        Dictionary with the arguments/context to be used when the template is rendered. Do not explicitly specify this
695        argument. This represents any other variable that will be sent to the template rendering system. Please
696        see an example below! In both ``ntp_peers_example_using_pillar`` and ``ntp_peers_example``, ``peers`` is sent as
697        template variable.
698
699        .. note::
700            It is more recommended to use the ``context`` argument instead, to
701            avoid any conflicts with other arguments.
702
703    SLS Example (e.g.: under salt://router/config.sls) :
704
705    .. code-block:: yaml
706
707        whole_config_example:
708            netconfig.managed:
709                - template_name: salt://path/to/complete_config.jinja
710                - debug: True
711                - replace: True
712        bgp_config_example:
713            netconfig.managed:
714                - template_name: /absolute/path/to/bgp_neighbors.mako
715                - template_engine: mako
716        prefix_lists_example:
717            netconfig.managed:
718                - template_name: prefix_lists.cheetah
719                - debug: True
720                - template_engine: cheetah
721        ntp_peers_example:
722            netconfig.managed:
723                - template_name: http://bit.ly/2gKOj20
724                - skip_verify: False
725                - debug: True
726                - peers:
727                    - 192.168.0.1
728                    - 192.168.0.1
729        ntp_peers_example_using_pillar:
730            netconfig.managed:
731                - template_name: http://bit.ly/2gKOj20
732                - peers: {{ pillar.get('ntp.peers', []) }}
733
734    Multi template example:
735
736    .. code-block:: yaml
737
738        hostname_and_ntp:
739          netconfig.managed:
740            - template_name:
741                - https://bit.ly/2OhSgqP
742                - https://bit.ly/2M6C4Lx
743                - https://bit.ly/2OIWVTs
744            - debug: true
745            - context:
746                hostname: {{ opts.id }}
747                servers:
748                  - 172.17.17.1
749                  - 172.17.17.2
750                peers:
751                  - 192.168.0.1
752                  - 192.168.0.2
753
754    Usage examples:
755
756    .. code-block:: bash
757
758        $ sudo salt 'juniper.device' state.sls router.config test=True
759
760        $ sudo salt -N all-routers state.sls router.config pillar="{'debug': True}"
761
762    ``router.config`` depends on the location of the SLS file (see above). Running this command, will be executed all
763    five steps from above. These examples above are not meant to be used in a production environment, their sole purpose
764    is to provide usage examples.
765
766    Output example:
767
768    .. code-block:: bash
769
770        $ sudo salt 'juniper.device' state.sls router.config test=True
771        juniper.device:
772        ----------
773                  ID: ntp_peers_example_using_pillar
774            Function: netconfig.managed
775              Result: None
776             Comment: Testing mode: Configuration discarded.
777             Started: 12:01:40.744535
778            Duration: 8755.788 ms
779             Changes:
780                      ----------
781                      diff:
782                          [edit system ntp]
783                               peer 192.168.0.1 { ... }
784                          +    peer 172.17.17.1;
785                          +    peer 172.17.17.3;
786
787        Summary for juniper.device
788        ------------
789        Succeeded: 1 (unchanged=1, changed=1)
790        Failed:    0
791        ------------
792        Total states run:     1
793        Total run time:   8.756 s
794
795    Raw output example (useful when the output is reused in other states/execution modules):
796
797    .. code-block:: bash
798
799        $ sudo salt --out=pprint 'juniper.device' state.sls router.config test=True debug=True
800
801    .. code-block:: python
802
803        {
804            'juniper.device': {
805                'netconfig_|-ntp_peers_example_using_pillar_|-ntp_peers_example_using_pillar_|-managed': {
806                    '__id__': 'ntp_peers_example_using_pillar',
807                    '__run_num__': 0,
808                    'already_configured': False,
809                    'changes': {
810                        'diff': '[edit system ntp]   peer 192.168.0.1 { ... }+   peer 172.17.17.1;+   peer 172.17.17.3;'
811                    },
812                    'comment': 'Testing mode: Configuration discarded.',
813                    'duration': 7400.759,
814                    'loaded_config': 'system {  ntp {  peer 172.17.17.1;  peer 172.17.17.3; } }',
815                    'name': 'ntp_peers_example_using_pillar',
816                    'result': None,
817                    'start_time': '12:09:09.811445'
818                }
819            }
820        }
821    """
822    ret = salt.utils.napalm.default_ret(name)
823
824    # the user can override the flags the equivalent CLI args
825    # which have higher precedence
826    test = test or __opts__["test"]
827    debug = __salt__["config.merge"]("debug", debug)
828    commit = __salt__["config.merge"]("commit", commit)
829    replace = __salt__["config.merge"]("replace", replace)  # this might be a bit risky
830    skip_verify = __salt__["config.merge"]("skip_verify", skip_verify)
831    commit_in = __salt__["config.merge"]("commit_in", commit_in)
832    commit_at = __salt__["config.merge"]("commit_at", commit_at)
833    revert_in = __salt__["config.merge"]("revert_in", revert_in)
834    revert_at = __salt__["config.merge"]("revert_at", revert_at)
835
836    config_update_ret = _update_config(
837        template_name=template_name,
838        template_source=template_source,
839        template_hash=template_hash,
840        template_hash_name=template_hash_name,
841        saltenv=saltenv,
842        template_engine=template_engine,
843        skip_verify=skip_verify,
844        context=context,
845        defaults=defaults,
846        test=test,
847        commit=commit,
848        commit_in=commit_in,
849        commit_at=commit_at,
850        revert_in=revert_in,
851        revert_at=revert_at,
852        debug=debug,
853        replace=replace,
854        **template_vars
855    )
856
857    return salt.utils.napalm.loaded_ret(ret, config_update_ret, test, debug)
858
859
860def commit_cancelled(name):
861    """
862    .. versionadded:: 2019.2.0
863
864    Cancel a commit scheduled to be executed via the ``commit_in`` and
865    ``commit_at`` arguments from the
866    :py:func:`net.load_template <salt.modules.napalm_network.load_template>` or
867    :py:func:`net.load_config <salt.modules.napalm_network.load_config>`
868    execution functions. The commit ID is displayed when the commit is scheduled
869    via the functions named above.
870
871    State SLS Example:
872
873    .. code-block:: yaml
874
875        '20180726083540640360':
876          netconfig.commit_cancelled
877    """
878    cancelled = {"name": name, "result": None, "changes": {}, "comment": ""}
879    if __opts__["test"]:
880        cancelled["comment"] = "It would cancel commit #{}".format(name)
881        return cancelled
882    ret = __salt__["net.cancel_commit"](name)
883    cancelled.update(ret)
884    return cancelled
885
886
887def commit_confirmed(name):
888    """
889    .. versionadded:: 2019.2.0
890
891    Confirm a commit scheduled to be reverted via the ``revert_in`` and
892    ``revert_at`` arguments from the
893    :mod:`net.load_template <salt.modules.napalm_network.load_template>` or
894    :mod:`net.load_config <salt.modules.napalm_network.load_config>`
895    execution functions. The commit ID is displayed when the commit confirmed
896    is scheduled via the functions named above.
897
898    State SLS Example:
899
900    .. code-block:: yaml
901
902        '20180726083540640360':
903          netconfig.commit_confirmed
904    """
905    confirmed = {"name": name, "result": None, "changes": {}, "comment": ""}
906    if __opts__["test"]:
907        confirmed["comment"] = "It would confirm commit #{}".format(name)
908        return confirmed
909    ret = __salt__["net.confirm_commit"](name)
910    confirmed.update(ret)
911    return confirmed
912